標籤:

Node.js 中 setTimeout(f1, 0) 與 setImmediate(f2) ,f1 f2的執行順序是隨機的嗎,為什麼呢?

測試代碼:

多次運行結果:


瀉藥

真心不會 node 啊……

能假裝沒看見么,

剛改完 PHP 回來,

而且都大半夜了 = =|||

佔個坑明兒再說?

洗洗回來,還是忍不住簡單寫寫。

話說還真是得看文檔。

但是更關鍵的是得看源碼,哭。

否則也不好找文檔哪兒說。

怎麼看代碼先不寫了。困,老了,實在扛不住。

ps:node 代碼還老改來改去,每次都得重找還。

setImmediate 先說這個,可以參考這裡的部分:

Process.nextTick 和 setImmediate 的區別? - Node.js

但是,由於 現在 node 代碼又改來改去,說代碼的部分就不用細看了。

主要看 libuv 的 uv_check_t , setImmediate 最終是調用的它。

根據文檔說明:

Check handles will run the given callback once per loop iteration, right after polling for i/o.

這貨會在IO處理之後回調

setTimout 最終調用 libuv 的 uv_timer_start。

這玩意有個陷阱,說是如果timout 是 0 就下一個事件循環調用。

If timeout is zero, the callback fires on the next event loop iteration.

但是吧,去看 node 里的 times.js 實現。

exports.setTimeout = function(callback, after) {
var timer;

after *= 1; // coalesce to number or NaN

if (!(after &>= 1 after &<= TIMEOUT_MAX)) { after = 1; // schedule on next tick, follows browser behaviour } timer = new Timeout(after); ... }

timeout 為 0 時候就給規約為 1 了。

所以呢,他還是得在事件循環時候參與計時,如果計時達到就觸發回調。

那好,一個事件循環里要按順序整多少事兒呢?

根據官方文檔可知:

  1. 先跑定時器 due

  2. ……

  3. 然後 idle (也就是nextTick)

  4. ……

  5. 然後IO

  6. 之後才到 check (也就是 setImmediate)

  7. ……

  8. 一次循環完

事件循環每一次都有個時間間隔。

這取決於當前 CPU 給分配的時間片長短。

它可能再1ms 內循環多次,也可能大於 1ms 才循環一次。

好了,定時器先跑時候肯定要看看到沒到時間,有可能這次循環時候就差個 0.2 ms,那麼 setTimeout 0(其實是1)的計時器回調就被忽略。沒掉點么不是。

然後跑啊跑,到 check setImmediate 必然執行。

最後跑到一次事件循環結束。

下次事件循環開始。

跑到setTimeout 0(其實是1),時間到了(或過了),回調立即觸發。

這就是 setImmediate 先 setTimeout 0 後的情況。

同樣,取決於事件循環第一次定時器時間比對,如果 setTimeout 0(其實是1)在當前事件循環恰好時間到了(或過了),回調立即觸發。

然後跑啊跑,到 check setImmediate 必然執行。

這就是 setTimeout 0 先 setImmediate 後的情況。

偶覺得是說清楚了,收工碎叫。

ps:

1、libuv 文檔:Welcome to the libuv API documentation

2、居然看見有發阮大文章的……內個吧,起碼發的內篇不真……具體對比看吧,不說了,怕被噴

3、如果要跟代碼,看 timers.js 和 http://timer_wrap.cc 基本就能跟差不多了,再跟下去得去 libuv 里找。

======================= 補充 =============================

有說這例子里 console.log 輸出是非同步的,所以測試代碼有問題,才導致這種情況。

根據 node 文檔,說通常是同步的,這詞太含糊,得實際跟下代碼。

然後跟了下代碼。

關鍵流程是:

在控制台下,會走入 TTY (中斷)判斷

然後 TTY 下會走到 stream_wrap.c 的 StreamWrapCallbacks::TryWrite 輸出 (node.js)

之後進入 uv_try_write (libuv) -&> uv__write -&> write 輸出

write 是 C 標準庫函數

整個過程無任何非同步。

所以測試代碼沒問題。

如果不放心把 consoel.log

換成 arr.push

這肯定是同步的

然後看 arr 數組順序

一樣是不固定的。


1. 請看文檔

2. 請自己寫個代碼實際測試下

3. 善用搜索(google、stackoverflow、GitHub)

以上三個都做了還找不到答案再提問。

[補充]你的測試代碼有問題。問題在哪兒自己想想看。


好像就是就是不確定的,這裡有篇博文,可以了解一下event loop:

http://www.ruanyifeng.com/blog/2014/10/event-loop.html

純搬運了……


setTimeout 將任務加入到了事件循環中,而nextTick加入到了回調隊列中


setTimeout不一定是什麼順序,不過nextTick和setImmediate是確定的


推薦閱讀:

為什麼 Node.js 不給每一個.js文件以獨立的上下文來避免作用域被污染?
為什麼nodejs不給每一個.js文件以獨立的上下文來避免作用域被污染?
nodejs中,zlib.gzip系列純cpu計算函數為什麼會有非同步版本?
es6 import from xx , 是怎麼實現找到 node_modules目錄下的?

TAG:Nodejs |