nodejs的特色html
nodejs 具備事件驅動和非阻塞I/O的特色。node
事件驅動是指nodejs把每個任務當成事件來處理。socket
非阻塞I/O是指nodejs遇到I/O任務時,會從線程池調度單獨的線程處理I/O操做,不會阻塞主線程。函數
事件循環原理oop
Node.js 在主線程裏維護了一個事件隊列,當接到請求後,就將該請求做爲一個事件放入這個隊列中,而後繼續接收其餘請求。spa
當主線程空閒時(沒有請求接入時),就開始循環事件隊列,檢查隊列中是否有要處理的事件,這時要分兩種狀況:線程
若是是非 I/O 任務,就親自處理,並經過回調函數返回到上層調用;code
若是是 I/O 任務,就從 線程池 中拿出一個線程來處理這個事件,並指定回調函數,而後繼續循環隊列中的其餘事件。htm
當線程中的 I/O 任務完成之後,就執行指定的回調函數,並把這個完成的事件放到事件隊列的尾部,等待事件循環,當主線程再次循環到該事件時,就直接處理並返回給上層調用。blog
流程圖
每次循環的六個階段
timers階段:這個階段執行定時器隊列中的回調,如 setTimeout()
和 setInterval()
。
I/O callbacks: 這個階段執行幾乎全部的回調。可是不包括close事件,定時器和setImmediate()
的回調。
idle, prepare: 這個階段僅在內部使用,能夠沒必要理會。
poll: 等待新的I/O事件,node在一些特殊狀況下會阻塞在這裏。
check: setImmediate()
的回調會在這個階段執行。
close callbacks: 例如socket.on('close', ...)
這種close事件的回調。
下面咱們來按照代碼第一次進入libuv引擎後的順序來詳細解說這些階段:
當個v8引擎將js代碼解析後傳入libuv引擎後,循環首先進入poll階段。
poll階段的執行邏輯以下:
先查看poll queue中是否有事件,有事件就按先進先出的順序依次執行回調。
當queue爲空時,會檢查是否有setImmediate()的callback,若是有就進入check階段執行這些callback。
當queue爲空時,同時也會檢查是否有到期的timer,若是有,就把這些到期的timer的callback按照調用順序放到timer queue中,以後循環會進入timer階段執行queue中的 callback。
這二者的順序是不固定的,收到代碼運行的環境的影響。
若是二者的queue都是空的,那麼loop會在poll階段停留,直到有一個i/o事件返回,循環會進入i/o callback階段並當即執行這個事件的callback。
值得注意的是,poll階段在執行poll queue中的回調時實際上不會無限的執行下去。
有兩種狀況poll階段會終止執行poll queue中的下一個回調:1.全部回調執行完畢。2.執行數超過了node的限制。
參考連接: