搜尋
首頁web前端js教程詳解nodejs中的事件循環機制

詳解nodejs中的事件循環機制

Apr 29, 2021 am 10:47 AM
nodejs事件循環

這篇文章帶大家了解一下node中的事件循環機制。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

詳解nodejs中的事件循環機制

前端開發離不開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事件循環已經很清楚了,為了比較這裡簡單再提一下。

詳解nodejs中的事件循環機制

(轉引自Philip Roberts的演講《Help, I'm stuck in an event-loop》)

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 {: 2},3,promise1,setTimeout1,promise2,setTimeout2。

瀏覽器裡執行結果如下:

詳解nodejs中的事件循環機制

#三、node事件循環

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

詳解nodejs中的事件循環機制

六个阶段分别是:

  • 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输出结果确实是这样:

詳解nodejs中的事件循環機制

那如下代码会输出什么呢?

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运行的结果:

詳解nodejs中的事件循環機制

可以看到两次执行的结果不一样,为什么呢?原因在于即使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中文網其他相關文章!

陳述
本文轉載於:掘金社区。如有侵權,請聯絡admin@php.cn刪除
JavaScript在行動中:現實世界中的示例和項目JavaScript在行動中:現實世界中的示例和項目Apr 19, 2025 am 12:13 AM

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

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

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

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

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

Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

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

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

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

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

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

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

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

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

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

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱工具

mPDF

mPDF

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

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

MantisBT

MantisBT

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

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用