詳細解讀Node 定時器知識
這篇文章主要介紹了Node 定時器的相關知識,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
JavaScript 是單線程運行,非同步操作特別重要。
只要用到引擎以外的功能,就需要跟外部交互,從而形成非同步操作。由於非同步操作實在太多,JavaScript 不得不提供許多非同步語法。這就好比,有些人老是受打擊, 他的抗打擊能力必須變得很強,否則他就完蛋了。
Node 的非同步語法比瀏覽器更複雜,因為它可以跟核心對話,不得不搞了一個專門的函式庫 libuv 來做這件事。這個函式庫負責各種回呼函數的執行時間,畢竟非同步任務最後還是要回到主線程,一個個排隊執行。
為了協調非同步任務,Node 居然提供了四個計時器,讓任務可以在指定的時間運行。
setTimeout()
#setInterval()
setImmediate()
#process.nextTick()
前兩者是語言的標準,後兩個是Node 獨有的。它們的寫法差不多,作用也差不多,不太容易差別。
你能說出下面程式碼的運行結果嗎?
// test.js setTimeout(() => console.log(1)); setImmediate(() => console.log(2)); process.nextTick(() => console.log(3)); Promise.resolve().then(() => console.log(4)); (() => console.log(5))();
運行結果如下。
$ node test.js
如果你能一口說對,可能就不需要再看下去了。本文詳細解釋,Node 怎麼處理各種定時器,或者更廣義地說,libuv 函式庫怎麼安排非同步任務在主執行緒上執行。
一、同步任務和非同步任務
#首先,同步任務總是比非同步任務更早執行。
前面的那段程式碼,只有最後一行是同步任務,因此最早執行。
(() => console.log(5))();
#二、本輪迴圈與次輪迴圈
非同步任務可以分成兩種。
追加在本輪循環的非同步任務
追加在次輪循環的非同步任務
所謂」循環”,指的是事件循環(event loop)。這是 JavaScript 引擎處理非同步任務的方式,後文會詳細解釋。這裡只要理解,本輪循環一定早於次輪循環執行即可。
Node 規定,process.nextTick和Promise的回呼函數,追加在本輪循環,即同步任務一旦執行完成,就開始執行它們。而setTimeout、setInterval、setImmediate的回呼函數,追加在次輪循環。
這就是說,文首那段程式碼的第三行和第四行,一定比第一行和第二行更早執行。
// 下面两行,次轮循环执行 setTimeout(() => console.log(1)); setImmediate(() => console.log(2)); // 下面两行,本轮循环执行 process.nextTick(() => console.log(3)); Promise.resolve().then(() => console.log(4));
三、process.nextTick()
process.nextTick這個名字有點誤導,它是在本輪循環執行的,而且是所有非同步任務裡面最快執行的。
Node 執行完所有同步任務,接下來就會執行process.nextTick的任務佇列。所以,下面這行程式碼是第二個輸出結果。
process.nextTick(() => console.log(3));
基本上,如果你希望非同步任務盡可能快地執行,那就使用process.nextTick。
四、微任務
根據語言規格,Promise
物件的回呼函數,會進入非同步任務裡面的」微任務」(microtask
)隊列。
微任務佇列追加在process.nextTick
佇列的後面,也屬於本輪迴圈。所以,下面的程式碼總是先輸出3,再輸出4。
process.nextTick(() => console.log(3)); Promise.resolve().then(() => console.log(4)); // 3 // 4
注意,只有前一個佇列全部清空以後,才會執行下一個佇列。
process.nextTick(() => console.log(1)); Promise.resolve().then(() => console.log(2)); process.nextTick(() => console.log(3)); Promise.resolve().then(() => console.log(4)); // 1 // 3 // 2 // 4
上面程式碼中,全部process.nextTick
的回呼函數,執行都會早於Promise
的。
至此,本輪循環的執行順序就講完了。
同步任务 process.nextTick() 微任务
五、事件循環的概念
#下面開始介紹次輪循環的執行順序,這必須理解什麼是事件循環( event loop)了。
Node 的官方文件是這樣介紹的。
“When Node.js starts, it initializes the event loop, processes the provided input script which may make async API calls, schedule timers, or call process.nextTick(), then begins processing the event loop.”
這段話很重要,需要仔細讀。它表達了三層意思。
首先,有些人以為,除了主線程,還存在一個單獨的事件循環線程。不是這樣的,只有一個主線程,事件循環是在主線程上完成的。
其次,Node 开始执行脚本时,会先进行事件循环的初始化,但是这时事件循环还没有开始,会先完成下面的事情。
同步任务
发出异步请求
规划定时器生效的时间
执行process.nextTick()等等
最后,上面这些事情都干完了,事件循环就正式开始了。
六、事件循环的六个阶段
事件循环会无限次地执行,一轮又一轮。只有异步任务的回调函数队列清空了,才会停止执行。
每一轮的事件循环,分成六个阶段。这些阶段会依次执行。
timers
I/O callbacks
idle, prepare
poll
check
close callbacks
每个阶段都有一个先进先出的回调函数队列。只有一个阶段的回调函数队列清空了,该执行的回调函数都执行了,事件循环才会进入下一个阶段。
下面简单介绍一下每个阶段的含义,详细介绍可以看官方文档,也可以参考 libuv 的源码解读。
(1)timers
这个是定时器阶段,处理setTimeout()和setInterval()的回调函数。进入这个阶段后,主线程会检查一下当前时间,是否满足定时器的条件。如果满足就执行回调函数,否则就离开这个阶段。
(2)I/O callbacks
除了以下操作的回调函数,其他的回调函数都在这个阶段执行。
setTimeout()和setInterval()的回调函数
setImmediate()的回调函数
用于关闭请求的回调函数,比如socket.on('close', ...)
(3)idle, prepare
该阶段只供 libuv 内部调用,这里可以忽略。
(4)Poll
这个阶段是轮询时间,用于等待还未返回的 I/O 事件,比如服务器的回应、用户移动鼠标等等。
这个阶段的时间会比较长。如果没有其他异步任务要处理(比如到期的定时器),会一直停留在这个阶段,等待 I/O 请求返回结果。
(5)check
该阶段执行setImmediate()的回调函数。
(6)close callbacks
该阶段执行关闭请求的回调函数,比如socket.on('close', ...)
。
七、事件循环的示例
下面是来自官方文档的一个示例。
const fs = require('fs'); const timeoutScheduled = Date.now(); // 异步任务一:100ms 后执行的定时器 setTimeout(() => { const delay = Date.now() - timeoutScheduled; console.log(`${delay}ms`); }, 100); // 异步任务二:至少需要 200ms 的文件读取 fs.readFile('test.js', () => { const startCallback = Date.now(); while (Date.now() - startCallback < 200) { // 什么也不做 } });
上面代码有两个异步任务,一个是 100ms 后执行的定时器,一个是至少需要 200ms 的文件读取。请问运行结果是什么?
脚本进入第一轮事件循环以后,没有到期的定时器,也没有已经可以执行的 I/O 回调函数,所以会进入 Poll 阶段,等待内核返回文件读取的结果。由于读取小文件一般不会超过 100ms,所以在定时器到期之前,Poll 阶段就会得到结果,因此就会继续往下执行。
第二轮事件循环,依然没有到期的定时器,但是已经有了可以执行的 I/O 回调函数,所以会进入 I/O callbacks 阶段,执行fs.readFile的回调函数。这个回调函数需要 200ms,也就是说,在它执行到一半的时候,100ms 的定时器就会到期。但是,必须等到这个回调函数执行完,才会离开这个阶段。
第三轮事件循环,已经有了到期的定时器,所以会在 timers 阶段执行定时器。最后输出结果大概是200多毫秒。
八、setTimeout 和 setImmediate
由于setTimeout
在 timers 阶段执行,而setImmediate
在 check 阶段执行。所以,setTimeout会早于setImmediate完成。
setTimeout(() => console.log(1)); setImmediate(() => console.log(2));
上面代码应该先输出1,再输出2,但是实际执行的时候,结果却是不确定,有时还会先输出2,再输出1。
这是因为setTimeout的第二个参数默认为0。但是实际上,Node 做不到0毫秒,最少也需要1毫秒,根据官方文档,第二个参数的取值范围在1毫秒到2147483647毫秒之间。也就是说,setTimeout(f, 0)
等同于setTimeout(f, 1)
。
实际执行的时候,进入事件循环以后,有可能到了1毫秒,也可能还没到1毫秒,取决于系统当时的状况。如果没到1毫秒,那么 timers 阶段就会跳过,进入 check 阶段,先执行setImmediate的回调函数。
但是,下面的代码一定是先输出2,再输出1。
const fs = require('fs'); fs.readFile('test.js', () => { setTimeout(() => console.log(1)); setImmediate(() => console.log(2)); });
上面代码会先进入 I/O callbacks 阶段,然后是 check 阶段,最后才是 timers 阶段。因此,setImmediate才会早于setTimeout执行。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
以上是詳細解讀Node 定時器知識的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

nvm刪除node的方法:1、下載「nvm-setup.zip」並將其安裝在C碟;2、設定環境變量,並透過「nvm -v」指令查看版本號;3、使用「nvm install」指令安裝node;4、透過「nvm uninstall」指令刪除已安裝的node即可。

怎麼處理文件上傳?以下這篇文章為大家介紹一下node專案中如何使用express來處理文件的上傳,希望對大家有幫助!

這篇文章跟大家分享Node的進程管理工具“pm2”,聊聊為什麼需要pm2、安裝和使用pm2的方法,希望對大家有幫助!

PiNetwork節點詳解及安裝指南本文將詳細介紹PiNetwork生態系統中的關鍵角色——Pi節點,並提供安裝和配置的完整步驟。 Pi節點在PiNetwork區塊鏈測試網推出後,成為眾多先鋒積極參與測試的重要環節,為即將到來的主網發布做準備。如果您還不了解PiNetwork,請參考Pi幣是什麼?上市價格多少? Pi用途、挖礦及安全性分析。什麼是PiNetwork? PiNetwork項目始於2019年,擁有其專屬加密貨幣Pi幣。該項目旨在創建一個人人可參與

身份驗證是任何網路應用程式中最重要的部分之一。本教程討論基於令牌的身份驗證系統以及它們與傳統登入系統的差異。在本教程結束時,您將看到一個用Angular和Node.js編寫的完整工作演示。傳統身份驗證系統在繼續基於令牌的身份驗證系統之前,讓我們先來看看傳統的身份驗證系統。使用者在登入表單中提供使用者名稱和密碼,然後點擊登入。發出請求後,透過查詢資料庫在後端驗證使用者。如果請求有效,則使用從資料庫中獲取的使用者資訊建立會話,然後在回應頭中傳回會話訊息,以便將會話ID儲存在瀏覽器中。提供用於存取應用程式中受

npm node gyp失敗是因為“node-gyp.js”跟“Node.js”版本不匹配,其解決辦法:1、透過“npm cache clean -f”清除node快取;2、透過“npm install -g n”安裝n模組;3、透過「n v12.21.0」指令安裝「node v12.21.0」版本即可。

您可以在 iPhone 相機上設定多長時間的定時器?當您在 iPhone 的相機應用程式中存取定時器選項時,您將獲得在兩種模式之間進行選擇的選項:3 秒 (3s)和10 秒 (10s)。當您手持 iPhone 時,您可以使用第一個選項從前置或後置相機快速自拍。第二個選項在場景中很有用,可以在遠處將 iPhone 安裝到三腳架上來點擊合照或自拍。如何在 iPhone 相機上設定定時器 雖然在 iPhone 相機上設定定時器是一個相當簡單的過程,但具體操作方式因所使用的 iPhone 機型而異。

什麼是單一登入系統?用nodejs怎麼實作?以下這篇文章為大家介紹一下使用node實作單一登入系統的方法,希望對大家有幫助!
