[英]How can a script and setTimeout/setInterval work together in NodeJS?
通讀NodeJS事件循環說明,我想知道setTimeout
和setInterval
如何真正起作用。
頁面上說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服務器時,請求會創建一個新的線程來處理該線程,並且當主腳本 (如您所稱)結束時,該線程將被殺死,並且該服務器上沒有其他任何內容(監聽請求的網絡服務器除外,例如nginx
或apache
)。
使用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在他的回答中所說的那樣, setTimeout
和setInterval
只是節點在超時時間段后運行回調的提示,沒有保證。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.