2012-11-28 57 views
20

我想知道是否有人知道如何在node.js中實現setTimeout。我相信我已經讀過某些地方,這不是V8的一部分。我很快試圖找到實現,但無法在源代碼(BIG)中找到它。例如,我找到了這個timers.js文件,然後例如鏈接到timer_wrap.cc。但是這些文件並沒有完全回答我的所有問題。setTimeout如何在node.js中實現

  • 請問V8有setTimeout執行嗎?我想也是從源頭上得出的答案是否定的。
  • setTimeout如何實施? JavaScript或本地或兩者的組合?從timers.js我想沿着這兩個線的東西:

    var Timer = process.binding('timer_wrap').Timer;` 
    
  • 當添加多個定時器(setTimeout的)怎麼做的node.js知道哪個先執行?它是否將所有定時器添加到集合(排序)?如果它被排序,那麼找到需要執行的超時是O(1)和O(log n)來插入?但是,然後再次在timers.js我看到他們使用鏈表?

  • 但是,然後再添加很多定時器是不是一個問題呢?
  • 在執行這個腳本:

    var x = new Array(1000), 
        len = x.length; 
    
    /** 
    * Returns a random integer between min and max 
    * Using Math.round() will give you a non-uniform distribution! 
    */ 
    function getRandomInt (min, max) { 
        return Math.floor(Math.random() * (max - min + 1)) + min; 
    } 
    
    var y = 0; 
    
    for (var i = 0; i < len; i++) { 
        var randomTimeout = getRandomInt(1000, 10000); 
    
        console.log(i + ', ' + randomTimeout + ', ' + ++y); 
        setTimeout(function() { 
         console.log(arguments); 
        }, randomTimeout, randomTimeout, y); 
    } 
    

    你的CPU使用率的一點點,但沒有那麼多嗎?

  • 我想知道如果我將在排序列表中逐一實現所有這些回調,如果我會得到更好的性能?

回答

17

你已經完成了大部分工作。 V8不提供setTimeout的實現,因爲它不是ECMAScript的一部分。您使用的函數在timers.js中實現,該函數創建一個圍繞C類的包裝對象Timeout的實例。

來源描述了他們如何管理定時器。

// Because often many sockets will have the same idle timeout we will not 
// use one timeout watcher per item. It is too much overhead. Instead 
// we'll use a single watcher for all sockets with the same timeout value 
// and a linked list. This technique is described in the libev manual: 
// http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts 

這表明它使用鏈接文章中#4的雙鏈表。

如果沒有一個請求,但成千上萬(百萬......),採用某種超時使用相同的超時值都 ,然後一個 可以做得更好:

啓動時超時時間,計算超時值,並將 超時放在列表的末尾。

然後當預計觸發 列表開始的超時時(例如,使用技術#3)時,使用ev_timer來觸發。

當有一些活動,從列表中刪除定時器, 重新計算超時,再次追加到列表的末尾,並 確保更新ev_timer如果它是從 的開始拍攝名單。

如此一來,一個可管理的超時爲O無限數量(1)時間 用於啓動,停止和更新定時器,在 主要併發症爲代價,並且不必使用恆定超時。常量 超時確保列表保持排序狀態。

Node.js是圍繞異步操作設計的,setTimeout是其中的重要組成部分。我不會試圖變得棘手,只是使用他們提供的東西。相信它足夠快,直到你證明在你的具體情況下它是一個瓶頸。不要卡在過早的優化。

UPDATE

會發生什麼事是你已經有了本質上是在頂層超時的字典,所以所有的100毫秒超時組合在一起。無論何時添加新的超時或最早的超時觸發器,它都會附加到列表中。這意味着最早的超時,即最快觸發的超時,是在列表的開頭。這個列表有一個計時器,它是根據直到列表中的第一個項目設置爲過期的時間設置的。

如果您撥打setTimeout每個具有相同超時值的1000次,它們將按照您呼叫setTimeout的順序附加到列表中,並且不需要進行排序。這是一個非常有效的設置。

+0

在這個例子中,超時總是一樣的? 60秒? – Alfred

+0

所以你希望在60秒內發生一些事情,並且你正在試圖弄清楚是把所有這些事情集合在一起,還是要爲每個事件分別創建一個setTimeout? –

+0

沒有抱歉。你鏈接的文件有60秒延遲。我的延誤可能是任何事情和很多! – Alfred

3

許多計時器沒有問題! 當uv循環調用輪詢時,它將所有定時器的最接近定時器的超時參數傳遞給它。

[最接近所有定時器的定時器]
https://github.com/joyent/node/blob/master/deps/uv/src/unix/timer.c#120

RB_MIN(uv__timers, &loop->timer_handles) 

[傳遞超時參數輪詢API]
https://github.com/joyent/node/blob/master/deps/uv/src/unix/core.c#276

timeout = 0; 
if ((mode & UV_RUN_NOWAIT) == 0) 
    timeout = uv_backend_timeout(loop); 

uv__io_poll(loop, timeout); 

注:上Windows操作系統,它幾乎是相同的邏輯