中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Node事件循環機制是什么

發布時間:2023-03-17 09:37:41 來源:億速云 閱讀:135 作者:iii 欄目:web開發

這篇文章主要介紹“Node事件循環機制是什么”,在日常操作中,相信很多人在Node事件循環機制是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Node事件循環機制是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

雖然js可以在瀏覽器中執行又可以在node中執行,但是它們的事件循環機制并不是一樣的。并且有很大的區別。

EventLoop機制概述

在說Node事件循環機制之前,我們先來討論兩個問題

為什么要學習事件循環機制?

學習事件循環可以讓開發者明白JavaScript的運行機制是怎么樣的。

事件循環機制做的是什么事情?

事件循環機制用于管理異步API的回調函數什么時候回到主線程中執行

Node.js采用的是異步IO模型。同步API在主線程中執行,異步API在底層的C++維護的線程中執行,異步API的回調函數也會在主線程中執行。【相關教程推薦:nodejs視頻教程、編程教學】

在Javascript應用運行時,眾多異步API的回調函數什么時候能回到主線程中調用呢?這就是事件環環機制做的事情,管理異步API的回調函數什么時候回到主線程中執行。

EventLoop的六個階段

Node中的事件循環分為六個階段。

Node事件循環機制是什么

在事件循環中的每個階段都有一個隊列,存儲要執行的回調函數,事件循環機制會按照先進先出的方式執行他們直到隊列為空。

這六個階段都存儲著異步回調函數,所以還是遵循先執行主線程同步代碼,當同步代碼執行完后再來輪詢這六個階段。

接下來,我們來詳細看看這六個階段里面存儲的都是什么

Timers

Timers:用于存儲定時器的回調函數(setlnterval,setTimeout)。

Pendingcallbacks

Pendingcallbacks:執行與操作系統相關的回調函數,比如啟動服務器端應用時監聽端口操作的回調函數就在這里調用。

idle,prepare

idle,prepare:系統內部使用。(這個我們程序員不用管)

Poll

Poll:存儲1/O操作的回調函數隊列,比如文件讀寫操作的回調函數。

在這個階段需要特別注意,如果事件隊列中有回調函數,則執行它們直到清空隊列 ,否則事件循環將在此階段停留一段時間以等待新的回調函數進入。

但是對于這個等待并不是一定的,而是取決于以下兩個條件:

  • 如果setlmmediate隊列(check階段)中存在要執行的調函數。這種情況就不會等待。

  • timers隊列中存在要執行的回調函數,在這種情況下也不會等待。事件循環將移至check階段,然后移至Closingcallbacks階段,并最終從timers階段進入下一次循環。

Check

Check:存儲setlmmediate的回調函數。

Closingcallbacks

Closingcallbacks:執行與關閉事件相關的回調,例如關閉數據庫連接的回調函數等。

宏任務與微任務

跟瀏覽器中的js一樣,node中的異步代碼也分為宏任務和微任務,只是它們之間的執行順序有所區別。

我們再來看看Node中都有哪些宏任務和微任務

宏任務

  • setlnterval

  • setimeout

  • setlmmediate

  • I/O

微任務

  • Promise.then

  • Promise.catch

  • Promise.finally

  • process.nextTick

node中,對于微任務和宏任務的執行順序到底是怎樣的呢?

微任務和宏任務的執行順序

node中,微任務的回調函數被放置在微任務隊列中,宏任務的回調函數被放置在宏任務隊列中。

微任務優先級高于宏任務。當微任務事件隊列中存在可以執行的回調函數時,事件循環在執行完當前階段的回調函數后會暫停進入事件循環的下一個階段,而會立即進入微任務的事件隊列中開始執行回調函數,當微任務隊列中的回調函數執行完成后,事件循環才會進入到下一個段開始執行回調函數。

對于微任務我們還有個點需要特別注意。那就是雖然nextTick同屬于微任務,但是它的優先級是高于其它微任務,在執行微任務時,只有nextlick中的所有回調函數執行完成后才會開始執行其它微任務。

總的來說就是當主線程同步代碼執行完畢后會優先清空微任務(如果微任務繼續產生微任務則會再次清空),然后再到下個事件循環階段。并且微任務的執行是穿插在事件循環六個階段中間的,也就是每次事件循環進入下個階段前會判斷微任務隊列是否為空,為空才會進入下個階段,否則先清空微任務隊列。

下面我們用代碼實操來驗證前面所說的。

代碼實例

先執行同步再執行異步

Node應用程序啟動后,并不會立即進入事件循環,而是先執行同步代碼,從上到下開始執行,同步API立即執行,異步API交給C++維護的線程執行,異步API的回調函數被注冊到對應的事件隊列中。當所有同步代碼執行完成后,才會進入事件循環。

console.log("start");

setTimeout(() => {
  console.log("setTimeout 1");
});

setTimeout(() => {
  console.log("setTimeout 2");
});

console.log("end");

我們來看執行結果

Node事件循環機制是什么

可以看到,先執行同步代碼,然后才會進入事件循環執行異步代碼,在timers階段執行兩個setTimeout回調。

setTimeout一定會先于setImmediate執行嗎

我們知道setTimeout是在timers階段執行,setImmediate是在check階段執行。并且事件循環是從timers階段開始的。所以會先執行setTimeout再執行setImmediate

對于上面的分析一定對嗎?

我們來看例子

console.log("start");

setTimeout(() => {
  console.log("setTimeout");
});

setImmediate(() => {
  console.log("setImmediate");
});

const sleep = (delay) => {
  const startTime = +new Date();
  while (+new Date() - startTime < delay) {
    continue;
  }
};

sleep(2000);
console.log("end");

執行上面的代碼,輸出如下

Node事件循環機制是什么

先執行setTimeout再執行setImmediate

接下來我們來改造下上面的代碼,把延遲器去掉,看看會輸出什么

setTimeout(() => {
  console.log("setTimeout");
});

setImmediate(() => {
  console.log("setImmediate");
});

我們運行了七次,可以看到其中有兩次是先運行的setImmediate

Node事件循環機制是什么

怎么回事呢?不是先timers階段再到check階段嗎?怎么會變呢?

其實這就得看進入事件循環的時候,異步回調有沒有完全準備好了。對于最開始的例子,因為有2000毫秒的延遲,所以進入事件循環的時候,setTimeout回調是一定準備好了的。所以執行順序不會變。但是對于這個例子,因為主線程沒有同步代碼需要執行,所以一開始就進入事件循環,但是在進入事件循環的時候,setTimeout的回調并不是一定完全準備好的,所以就會有先到check階段執行setImmediate回調函數,再到下一次事件循環的timers階段來執行setTimeout的回調。

那在什么情況下同樣的延遲時間,setImmediate回調函數一定會優先于setTimeout的回調呢?

其實很簡單,只要將這兩者放到timers階段和check階段之間的Pendingcallbacks、idle,prepare、poll階段中任意一個階段就可以了。因為這些階段完執行完是一定會先到check再到timers階段的。

我們以poll階段為例,將這兩者寫在IO操作中。

const fs = require("fs");

fs.readFile("./fstest.js", "utf8", (err, data) => {
  setTimeout(() => {
    console.log("setTimeout");
  });

  setImmediate(() => {
    console.log("setImmediate");
  });
});

我們也來執行七次,可以看到,每次都是setImmediate先執行。

Node事件循環機制是什么

所以總的來說,同樣的延遲時間,setTimeout并不是百分百先于setImmediate執行。

先微任務再宏任務

主線程同步代碼執行完畢后,會先執行微任務再執行宏任務。

我們來看下面的例子

console.log("start");

setTimeout(() => {
  console.log("setTimeout");
});

setImmediate(() => {
  console.log("setImmediate");
});

Promise.resolve().then(() => {
  console.log("Promise.resolve");
});

console.log("end");

我們運行一下看結果,可以看到它是先執行了微任務然后再執行宏任務

Node事件循環機制是什么

nextTick優于其它微任務

在微任務中nextTick的優先級是最高的。

我們來看下面的例子

console.log("start");

setTimeout(() => {
  console.log("setTimeout");
});

setImmediate(() => {
  console.log("setImmediate");
});

Promise.resolve().then(() => {
  console.log("Promise.resolve");
});

process.nextTick(() => {
  console.log("process.nextTick");
});

console.log("end");

我們運行上面的代碼,可以看到就算nextTick定義在resolve后面,它也是先執行的。

Node事件循環機制是什么

微任務穿插在各個階段間執行

怎么理解這個穿插呢?其實就是在事件循環的六個階段每個階段執行完后會清空微任務隊列。

我們來看例子,我們建立了timers、check、poll三個階段,并且每個階段都產生了微任務。

// timers階段
setTimeout(() => {
  console.log("setTimeout");

  Promise.resolve().then(() => {
    console.log("setTimeout Promise.resolve");
  });
});

// check階段
setImmediate(() => {
  console.log("setImmediate");
  Promise.resolve().then(() => {
    console.log("setImmediate Promise.resolve");
  });
});

// 微任務
Promise.resolve().then(() => {
  console.log("Promise.resolve");
});

// 微任務
process.nextTick(() => {
  console.log("process.nextTick");
  Promise.resolve().then(() => {
    console.log("nextTick Promise.resolve");
  });
});

我們來執行上面的代碼

Node事件循環機制是什么

可以看到,先執行微任務,再執行宏任務。先process.nextTick -> Promise.resolve。并且如果微任務繼續產生微任務則會再次清空,所以就又輸出了nextTick Promise.resolve

接下來到timer階段,輸出setTimeout,并且產生了一個微任務,再進入到下個階段前需要清空微任務隊列,所以繼續輸出setTimeout Promise.resolve

接下來到check階段,輸出setImmediate,并且產生了一個微任務,再進入到下個階段前需要清空微任務隊列,所以繼續輸出setImmediate Promise.resolve

這也就印證了微任務會穿插在各個階段之間運行。

Node事件循環機制是什么

到此,關于“Node事件循環機制是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

武邑县| 宜章县| 重庆市| 商南县| 东方市| 西乡县| 多伦县| 辽宁省| 股票| 英吉沙县| 九台市| 北辰区| 盐边县| 襄城县| 霞浦县| 吉林市| 同德县| 南丰县| 大名县| 丹江口市| 广宗县| 土默特左旗| 灵武市| 合山市| 漠河县| 宜阳县| 砚山县| 且末县| 教育| 荥阳市| 金溪县| 高青县| 巨野县| 西华县| 宁都县| 湖州市| 桐城市| 阿尔山市| 南安市| 凉城县| 泸水县|