細說setTimeout/setImmediate/process.nextTick的區別
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();
它的執行邏輯是,先詢問事件觀察者當前是否有任務需要執行?觀察者回答“有”,於是取出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"); });
兩者都代表主執行緒完成後立即執行,其執行結果是不確定的,可能是setTimeout回撥函式執行結果在前,也可能是setImmediate回撥函式執行結果在前,但setTimeout回撥函式執行結果在前的概率更大些,這是因為他們採用的觀察者不同,setTimeout採用的是類似IO觀察者,setImmediate採用的是check觀察者,而process.nextTick()採用的是idle觀察者。
三種觀察者的優先順序順序是:idle觀察者>>io觀察者>check觀察者
process.nextTick()與setImmediate()和setTimeout()的區別如下:
1、原始程式碼:
A();
B();
C();
它的執行順序即程式碼順序: