簡體   English   中英

腳本和setTimeout / setInterval如何在NodeJS中一起工作?

[英]How can a script and setTimeout/setInterval work together in NodeJS?

通讀NodeJS事件循環說明,我想知道setTimeoutsetInterval如何真正起作用。

頁面上說NodeJS首先運行給定的腳本(現在暫時不使用REPL), 然后進入事件循環。 但是,如果我在該腳本中調用setTimeout並期望腳本仍在運行時觸發該怎么辦? 那不是正常情況嗎? 根據描述,在主腳本結束之前不會觸發計時器回調,這對我來說真的很奇怪。

對於那些感興趣的人,這里是NodeJS外部偶數循環(實際上有2個嵌套循環): https : //github.com/nodejs/node/blob/master/src/node.cc#L4526

讓我們以身作則

setTimeout(function(){
    print('there');
});

print('hi');

這將打印hi然后there

這是發生了什么

該腳本將一直處理到最后一行,並且一旦找到計時器函數,它將把它添加到隊列中,稍后由隊列調度程序at the end of the execution時進行處理

loop queue => [ setTimeout ]

在退出之前,應該有一個調度程序,進行某種循環,以檢查隊列中是否有東西並處理它們,然后一旦所有計時器中都沒有隊列,循環就會退出。

假設我們在setInterval調用setTimeout

setInterval(function(){
    setTimeout(function(){
        print('hi')
    }, 500);
}, 1000);

loop queue => [ setInterval ]

1000 ms

將觸發setInterval並將內部setTimeout添加到隊列中

loop queue => [ setTimeout, setInterval ]

現在我們回到main loop ,它將再等待500毫秒觸發內部setTimeout函數,然后將其從隊列中刪除,因為setTimeout應該運行一次。

loop queue => [ setInterval ]

回到主循環,隊列中仍然有項目,因此它將等待另外500 ms並再次觸發( 500 + 500 = 1000 ms)內部setTimeout函數將再次添加到隊列中

loop queue => [ setTimeout, setInterval ]

回到主隊列再次...

現在,這只是計時器的工作方式,它們並不是要處理阻塞代碼,而是一種在一定間隔下運行代碼的方法

setInterval(function(){
    // do something long running here
    while (1) {}
    setTimeout(function(){
        print('hi')
    }, 500);
}, 1000);

主循環將在此處阻塞,並且內部超時不會添加到隊列中,所以這是一個壞主意

通常,nodejs和事件循環對網絡操作很有用,因為例如與select一起使用時它們不會阻塞。

setInterval(function(){
    // check if socket has something
    if (socketHasData( socket )){
        processSocketData( socket );
    }

    // do something else that does not block
    // maybe schedule another timer here
    print('hello');
}, 1000);

libuv是nodejs中使用的事件循環,它使用線程來處理一些阻塞操作,例如IO操作,打開/讀取/寫入文件

[編輯]哼哼重新閱讀您的第一篇文章,我想我知道您有什么毛病。 您在帖子中提到了nodejs ,這意味着您可能正在對服務器進行編碼。

如果您對服務器端JavaScript不太熟悉,而對php服務器更熟悉,例如,它可能確實令人困惑。

使用php服務器時,請求會創建一個新的線程來處理該線程,並且當主腳本 (如您所稱)結束時,該線程將被殺死,並且該服務器上沒有其他任何內容(監聽請求的網絡服務器除外,例如nginxapache )。

使用nodejs,就不同了。 主線程是單獨的,並且始終在運行 因此,當請求到達時,將觸發回調,但它們仍在該單個線程中。 否則說: 主腳本永遠不會結束 (除非您殺死它或服務器崩潰:))


好吧,這是准確的。 由於JavaScript 具有單線程特性,如果計時器在主線程忙時結束,則計時器的回調將等待

當你做

setTimeout(callback, 1000)

您不是在說“我希望在1秒內調用此回調”,而實際上是“我希望在1s內調用此回調”

John Resig撰寫的這篇文章非常出色,並詳細介紹了JavaScript計時器的詳細信息, 網址為https://johnresig.com/blog/how-javascript-timers-work/

但是,如果我在該腳本中調用setTimeout並期望腳本仍在運行時觸發該怎么辦?

你沒想到。 您希望同步代碼在超時發生之前運行到完成方式。

如果腳本仍在運行,因為它在進行阻止( 掛起) ,則超時回調沒有機會執行,它將等待。 這就是為什么我們需要編寫非阻塞代碼。

那不是正常情況嗎?

否。大多數情況下,沒有JS執行時,事件循環處於空閑狀態(盡管可能有后台任務在執行繁重的工作)。

鑒於Node是單線程的,因此它(v8引擎)始終在執行下一個腳本之前執行當前腳本。 因此,當我們使用主腳本啟動節點服務器時,它先加載,解析,編譯和執行該腳本,然后再運行其他腳本。 僅當當前正在運行的腳本命中I / O調用時,它才會被撞到事件循環的后面,從而為其他腳本或setTimeout回調提供執行的機會。 這是JavaScript引擎的本質,也是Node不適合長期運行於內存中的CPU密集型任務的原因。

就像@atomrc在他的回答中所說的那樣, setTimeoutsetInterval只是節點在超時時間段運行回調的提示,沒有保證。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM