這篇文章帶大家了解一下node中的事件循環機制。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。
前端開發離不開JavaScript,Javascript是一種web前端語言,主要用於web開發中,由瀏覽器解析執行。而js的作用不只限於前端領域的開發,它同樣可以用於服務端開發-nodejs。身為一個有理想抱負的前端,想要拓展視野,掌握一門伺服器端開發語言,那麼nodejs是非常好的選擇。
相關推薦:《nodejs 教學》
因為你掌握了js開發方式就很容易上手node,並且npm套件管理工具也大大提升了開發體驗。 nodejs以非同步非阻塞I/O工作方式而聞名,其處理機制稱為事件循環。
了解node事件循環機制就能更好的了解node的事件處理方式以及非同步事件的執行時機,本文主要講解一下nodejs的事件循環機制,為後續學習node奠定基礎。
一、node VS javascript
前面提到,Javascript是一種web前端語言,主要用於web開發中,由瀏覽器解析執行,而node .js 是一個基於Chrome V8 引擎的JavaScript 運行環境,因此nodejs不是一門語言,不是庫,不是框架,而是一個js運行時環境,簡單講node可以解析和執行js程式碼。以前只有瀏覽器可以解析執行Js,現在node可以讓js完全脫離瀏覽器來運作。
node.js和瀏覽器js有許多差異,例如瀏覽器中的js包含了ecmascript、BOM、DOM,但nodejs中的js沒有BOM,DOM,只有emcscript。並且node這個js執行環境為js提供了一些伺服器級別的操作API,例如:文件讀寫,網絡服務構建,網絡通信,http伺服器等,這些API大都被包裝到核心模組裡面了。另外node的事件循環機制和瀏覽器js的事件循環機制也不一樣。
二、JavaScript事件循環
大家對瀏覽器中的js事件循環已經很清楚了,為了比較這裡簡單再提一下。
js執行時同步和非同步任務任務分別進入不同的執行環境,同步任務的進入主線程,即主執行棧,非同步任務(ajax請求、settimeout、setinterval、poromise.resolve()等)進入任務佇列。不同的非同步任務會推入不同的任務佇列,例如ajax請求、settimeout、setinterval等這些任務會被推入進宏任務佇列(Macro Task),而Promise函數則會被推到微任務佇列(Micro Task) 。整體的事件循環過程如下:
當同步程式碼執行完後,主執行堆疊變空,開始準備執行非同步任務。
主執行緒會檢查微任務佇列是否為空,如果不為空那麼會遍歷佇列內的所有微任務將其執行完,清空微任務佇列,然後再檢查宏任務隊列。如果微任務隊列是空的,直接進入下一步。
主執行緒遍歷巨集任務佇列,並執行巨集任務佇列中的第一個巨集任務,在執行的過程中如果遇到巨集任務或微任務,則繼續將他們推入對應的任務佇列,每次執行完一次巨集任務都要遍歷執行一下微任務佇列,將其清空
執行渲染操作,更新視圖
#開始下一次的事件循環,重複上述步驟直至兩個任務隊列清空
#為了加深影響,舉一個小小的,看看以下程式碼會輸出什麼:
var le=Promise.resolve(2); console.log(le) console.log('3') Promise.resolve().then(()=>{ console.log('Promise1') setTimeout(()=>{ console.log('setTimeout2') },0) }) setTimeout(()=>{ console.log('setTimeout1') Promise.resolve().then(()=>{ console.log('Promise2') }) },0);
用以上的事件循環過程分析一下:
- js主程序執行程式碼遇到Promise.resolve(2),會立即執行,將2變成一個promise對象,然後console.log(le)將le變數印出來,印出----->Promise {
: 2}; - console.log('3') ,列印----->3
- 接著往下執行遇到Promise.resolve().then,這是一個非同步微任務函數,推到微任務堆疊
- 下一個函數遇到setTimeout,推到宏任務隊列,至此主進程空了
- 檢查微任務隊列,發現Promise.resolve().then,所以印出----->promise1,又遇到一個定時器,推至巨集任務佇列的最後,微任務佇列空了
- 檢查巨集任務佇列,取第一個巨集任務執行,列印----->setTimeout1,又遇到Promise.resolve().then,推至微任務隊列
- 再開始下一個宏任務之前,一定會清空微任務,因此印製setTimeout1後,便會檢查微任務隊列,於是---> ;promise2
- 接下來又一輪事件循環,取巨集任務佇列目前的第一個任務執行,於是列印列印----->setTimeout2,至此巨集任務和微任務佇列都已清空,事件循環結束
因此輸出結果是:Promise {
瀏覽器裡執行結果如下:

#三、node事件循環
node的事件循環共有六個階段,在一次事件循環中這六個階段依序會一直循環執行,直到事件處理完成。六個階段的順序圖如下:

六个阶段分别是:
- timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调
- I/O callbacks 阶段:执行一些系统操作的回调(比如网络通信的错误回调);
- idle, prepare 阶段:仅node内部使用,可忽略
- poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里
- check 阶段:执行 setImmediate() 的回调
- close callbacks 阶段:执行 socket 的 close 事件回调,如果一个socket或handle被突然关掉(比如socket.destroy()),close事件将在这个阶段被触发
事件循环中,每当进入某一个阶段,都会从该阶段对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,该阶段就会终止,然后检查NextTick队列和微任务队列,将其清空,之后进入下一个阶段。
这里面比较关键的是poll阶段:
- poll队列不为空的时候,事件循环会遍历队列并同步执行回调,直到队列清空或执行回调数达到系统上限。
- poll队列为空的时候,就会有两种情况:
- 如果代码中存在setImmediate()回调,那么事件循环直接结束poll阶段进入check阶段来执行check队列里的回调;
- 如果不存在setImmediate()回调,会等待回调被加入到队列中并立即执行回调,这里同样会有个超时时间设置防止一直等待下去,如果规定时间内有定时器函数进入队列,则返回到timer阶段,执行定时器回调,否则在poll阶段等待回调进入队列。
同样的举个大大的,看看以下代码会输出什么:
console.log('start') setTimeout(() => { console.log('timer1') Promise.resolve().then(function() { console.log('promise1') }) }, 0) setTimeout(() => { console.log('timer2') Promise.resolve().then(function() { console.log('promise2') }) }, 0) Promise.resolve().then(function() { console.log('promise3') }) console.log('end')
利用node事件循环分析呗:
- 先执行同步任务,打印start,end
- 进入timer阶段前,清空NextTick和micro队列,所以打印promise3
- 进入timer阶段,打印timer1,并发现有一个微任务,立即执行微任务,打印promise1
- 仍然在timer阶段,执行下个宏任务,打印timer2,同样遇到微任务,立即执行,打印promise2
因此输出顺序是:start,end,promise3,timer1,promise1,timer2,promise2,如果能正确回答出来说明对node的循环机制有了大体的了解,实际node输出结果确实是这样:

那如下代码会输出什么呢?
process.nextTick(function(){ console.log(7); }); new Promise(function(resolve){ console.log(3); resolve(); console.log(4); }).then(function(){ console.log(5); }); process.nextTick(function(){ console.log(8); });
继续分析:
- process.nextTick会将任务推进至nextTick队列,promise.then会把任务推至micro队列,上面提到过每次一个宏任务执行完,执行下一个宏任务之前需要清空nextTick队列和micro队列,同样的一个阶段执行完,进入下一个阶段之前也需要nextTick队列和micro队列,并且nextTick队列优先级高于micro队列
- 先执行同步代码,打印3,4
- 执行nextTick队列,打印7,8
- 再执行micro队列,打印5
因此最终输出是:3,4,7,8,5,需要记住,process.nextTick 永远大于 promise.then的优先级
还有一个大家很容易混淆的点就是setTimout和setImmediate的执行时机,根据上面描述的node事件循环机制,setImmediate()应该在check阶段执行 与 而setTimeout在timer阶段执行,理论上setTimout比setImmediate先执行,看下面的代码:
setTimeout(() => console.log(1),0); setImmediate(() => console.log(2));
执行结果是什么?1,2 还是 2,1,其实都有可能,看实际node运行的结果:

可以看到两次执行的结果不一样,为什么呢?原因在于即使setTimeout的第二个参数默认为0,但实际上,Node做不到0秒就执行其回调,最少也要4毫秒。那么进入事件循环后,如果没到4毫秒,那么timers阶段就会被跳过,从而进入check阶段执行setImmediate回调,此时输出结果是:2,1;
如果进入事件循环后,超过4毫秒(只是个大概,具体值并不确定),setTimeout的回调会出现在timer阶段的队列里,回调将被执行,之后再进入poll阶段和check阶段,此时输出结果是:1,2
那如果两者在I/O周期内调用,谁先执行呢?看一下代码:
const fs = require('fs') fs.readFile('./test.txt', 'utf8' , (err, data) => { if (err) { console.error(err) return } setTimeout(() => { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); }); })
实际上,node中输出的结果总是immediate先输出,timeout后输出。因为I/O回调是在poll阶段执行,当回调执行完毕之后队列为空,发现存在setImmediate的回调就会进入check阶段,执行完毕后,再进入timer阶段。
四、总结
本文结合代码示例,对node的事件循环机制做了比较详细描述。通过这篇文章,应该可以了解浏览器的事件循环机制是怎样的,node的循环机制是怎样的,以及nextTick和micro队列的优先级,setTimout和setImmediate执行时机等一些容易混淆的知识点。文章中不足和不对之处,欢迎在评论区交流讨论,一起探索,谢谢。
更多编程相关知识,请访问:编程入门!!
以上是詳解nodejs中的事件循環機制的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

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