node.js中的非IO的異步API提供了四種方法,分別為setTimeOut(),setInterval(),setImmediate()以及process.nextTick(),四種方法實現原理相似,但達到的效果略有區別:
一、事件循環Event Loop
首先,我們需要了解node.js的基於事件循環的事件模型,正是因為它才使得node.js中回調函數十分普遍,也正是基於此,node.js實現了單線程高效的異步IO(這里說的單線程主要說的是執行javascript代碼部分的線程,而異步IO部分node.js其實還是利用線程池去執行的)。
簡單的講就是,在node.js啟動時,創建了一個類似while(true)的循環體,每次執行一次循環體稱為一次tick,每個tick的過程就是查看是否有事件等待處理,如果有,則取出事件極其相關的回調函數並執行,然后執行下一次tick。所以,有如下代碼:
A(); B(); C();
- 1
- 2
- 3
它的執行邏輯是,先詢問事件觀察者當前是否有任務需要執行?觀察者回答“有”,於是取出A執行,A是否有回調函數?如果有(如果沒有則繼續詢問當前是否有任務需要執行),則取出回調函數並執行(注意:回調函數的執行基本都是異步的,可能不止一個回調),執行完回調后通過某種方式通知調用者,我執行完了,並把執行結果給你,你自己酌情處理吧,主函數不需要不斷詢問回調函數執行結果,回調函數會以通知的方式告知調用者我執行完了(don’t call me ,i will call you.),而這個過程主線程並不需要等待回調函數執行完成,它會繼續向前執行,即再次詢問觀察者當前是否還有任務需要執行,重復上面的步驟。。。直到觀察者回答沒有了,線程結束。
整個事件循環的邏輯如下圖:
二:setTimeOut(),setInterval(),setImmediate()以及process.nextTik()
這里面setTimeOut()與setInterval()除了執行頻次外基本相同,都表示主線程執行完一定時間后立即執行,而setImmediate()與之十分相似,也表示主線程執行完成后立即執行。那么他們之間的區別是什么呢?
代碼如下:
setTimeout(function(){ console.log("setTimeout"); },0); setImmediate(function(){ console.log("setImmediate"); });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
兩者都代表主線程完成后立即執行,其執行結果是不確定的,可能是setTimeout回調函數執行結果在前,也可能是setImmediate回調函數執行結果在前,但setTimeout回調函數執行結果在前的概率更大些,這是因為他們采用的觀察者不同,setTimeout采用的是類似IO觀察者,setImmediate采用的是check觀察者,而process.nextTick()采用的是idle觀察者。
三種觀察者的優先級順序是:idle觀察者>>io觀察者>check觀察者
process.nextTick()與setImmediate()和setTimeout()的區別如下:
1、原始代碼:
A(); B(); C();
- 1
- 2
- 3
它的執行順序即代碼順序:
2、process.nextTick()執行效果,代碼如下:
A(); process.nextTick(B); C();
- 1
- 2
- 3
它的執行順序如下:
3、setImmediate()或者setTimeout()執行效果,代碼如下:
A(); setImmediate(B);//或者setTimeout(B,0); C();
- 1
- 2
- 3
它的執行順序如下:
結論:
process.nextTick(),效率最高,消費資源小,但會阻塞CPU的后續調用;
setTimeout(),精確度不高,可能有延遲執行的情況發生,且因為動用了紅黑樹,所以消耗資源大;
setImmediate(),消耗的資源小,也不會造成阻塞,但效率也是最低的。