91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

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

nodejs中事件循環機制的示例分析

發布時間:2021-04-29 11:11:34 來源:億速云 閱讀:140 作者:小新 欄目:web開發

這篇文章主要介紹了nodejs中事件循環機制的示例分析,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

前端開發離不開JavaScript,Javascript是一種web前端語言,主要用于web開發中,由瀏覽器解析執行。而js的作用不僅僅局限于前端領域的開發,它同樣可以用于服務端開發——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 {<fulfilled>: 2};

  • console.log('3'),打印----->3

  • 接著往下執行遇到Promise.resolve().then,這是一個異步微任務函數,推到微任務棧

  • 下一個函數遇到setTimeout,推到宏任務隊列,至此主進程空了

  • 檢查微任務隊列,發現Promise.resolve().then,所以打印----->promise1,又遇到一個定時器,推至宏任務隊列的最后,微任務隊列空了

  • 檢查宏任務隊列,取第一個宏任務執行,打印----->setTimeout1,又遇到 Promise.resolve().then,推至微任務隊列

  • 再開始下一個宏任務之前,一定會清空微任務,因此打印setTimeout1后,便會檢查微任務隊列,于是--->promise2

  • 接下來又一輪事件循環,取宏任務隊列當前的第一個任務執行,于是打印打印----->setTimeout2,至此宏任務和微任務隊列均被清空,事件循環結束

因此輸出結果是:Promise {<fulfilled>: 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階段。

感謝你能夠認真閱讀完這篇文章,希望小編分享的“nodejs中事件循環機制的示例分析”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

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

AI

青浦区| 会同县| 三门峡市| 分宜县| 鸡泽县| 桓台县| 二连浩特市| 越西县| 屯留县| 新巴尔虎左旗| 叙永县| 镶黄旗| 苏尼特右旗| 禹州市| 莒南县| 宜黄县| 界首市| 石家庄市| 讷河市| 华亭县| 安多县| 利川市| 登封市| 武清区| 乌什县| 新蔡县| 玛纳斯县| 凤庆县| 长丰县| 昭苏县| 金门县| 博乐市| 仙居县| 大冶市| 通渭县| 乌拉特后旗| 炉霍县| 尖扎县| 龙井市| 电白县| 柳江县|