真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Node.js中事件循環(huán)機(jī)制的示例分析-創(chuàng)新互聯(lián)

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

成都創(chuàng)新互聯(lián)是一家專注于成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)與策劃設(shè)計(jì),石柱土家族網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)十載,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:石柱土家族等地區(qū)。石柱土家族做網(wǎng)站價(jià)格咨詢:18982081108

在瀏覽器篇已經(jīng)對(duì)事件循環(huán)機(jī)制和一些相關(guān)的概念作了詳細(xì)介紹,但主要是針對(duì)瀏覽器端的研究,Node環(huán)境是否也一樣呢?先看一個(gè)demo:

setTimeout(()=>{
    console.log('timer1')
    Promise.resolve().then(function() {
        console.log('promise1')
    })}, 0)setTimeout(()=>{
    console.log('timer2')
    Promise.resolve().then(function() {
        console.log('promise2')
    })}, 0)

肉眼編譯運(yùn)行一下,蒽,在瀏覽器的結(jié)果就是下面這個(gè)了,道理都懂,就不累述了。

timer1
promise1
timer2
promise2

那么Node下執(zhí)行看看,咦。。。奇怪,跟瀏覽器的運(yùn)行結(jié)果并不一樣~

timer1
timer2
promise1
promise2

例子說明,瀏覽器和 Node.js 的事件循環(huán)機(jī)制是有區(qū)別的,一起來看個(gè)究竟吧~

Node.js的事件處理

Node.js采用V8作為js的解析引擎,而I/O處理方面使用了自己設(shè)計(jì)的libuv,libuv是一個(gè)基于事件驅(qū)動(dòng)的跨平臺(tái)抽象層,封裝了不同操作系統(tǒng)一些底層特性,對(duì)外提供統(tǒng)一的API,事件循環(huán)機(jī)制也是它里面的實(shí)現(xiàn),核心源碼參考:

int uv_run(uv_loop_t* loop, uv_run_mode mode) {
  int timeout;
  int r;
  int ran_pending;
  r = uv__loop_alive(loop);
  if (!r)
    uv__update_time(loop);
  while (r != 0 && loop->stop_flag == 0) {
    uv__update_time(loop);
    // timers階段
    uv__run_timers(loop);
    // I/O callbacks階段
    ran_pending = uv__run_pending(loop);
    // idle階段
    uv__run_idle(loop);
    // prepare階段
    uv__run_prepare(loop);
    timeout = 0;
    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
      timeout = uv_backend_timeout(loop);
    // poll階段
    uv__io_poll(loop, timeout);
    // check階段
    uv__run_check(loop);
    // close callbacks階段
    uv__run_closing_handles(loop);
    if (mode == UV_RUN_ONCE) {
      uv__update_time(loop);
      uv__run_timers(loop);
    }
    r = uv__loop_alive(loop);
    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
      break;
  }
  if (loop->stop_flag != 0)
    loop->stop_flag = 0;
  return r;
}

根據(jù)Node.js官方介紹,每次事件循環(huán)都包含了6個(gè)階段,對(duì)應(yīng)到 libuv 源碼中的實(shí)現(xiàn),如下圖所示

Node.js中事件循環(huán)機(jī)制的示例分析

timers 階段:這個(gè)階段執(zhí)行timer(setTimeout、setInterval)的回調(diào)

I/O callbacks 階段:執(zhí)行一些系統(tǒng)調(diào)用錯(cuò)誤,比如網(wǎng)絡(luò)通信的錯(cuò)誤回調(diào)

idle, prepare 階段:僅node內(nèi)部使用

poll 階段:獲取新的I/O事件, 適當(dāng)?shù)臈l件下node將阻塞在這里

check 階段:執(zhí)行 setImmediate() 的回調(diào)

close callbacks 階段:執(zhí)行 socket 的 close 事件回調(diào)

我們重點(diǎn)看timers、poll、check這3個(gè)階段就好,因?yàn)槿粘i_發(fā)中的絕大部分異步任務(wù)都是在這3個(gè)階段處理的。

timers 階段

timers 是事件循環(huán)的第一個(gè)階段,Node 會(huì)去檢查有無已過期的timer,如果有則把它的回調(diào)壓入timer的任務(wù)隊(duì)列中等待執(zhí)行,事實(shí)上,Node 并不能保證timer在預(yù)設(shè)時(shí)間到了就會(huì)立即執(zhí)行,因?yàn)镹ode對(duì)timer的過期檢查不一定靠譜,它會(huì)受機(jī)器上其它運(yùn)行程序影響,或者那個(gè)時(shí)間點(diǎn)主線程不空閑。比如下面的代碼,setTimeout() 和 setImmediate() 的執(zhí)行順序是不確定的。

setTimeout(() => {
  console.log('timeout')
  }, 0)
  setImmediate(() => {
  console.log('immediate')
  })

但是把它們放到一個(gè)I/O回調(diào)里面,就一定是 setImmediate() 先執(zhí)行,因?yàn)閜oll階段后面就是check階段。

poll 階段

poll 階段主要有2個(gè)功能:

處理 poll 隊(duì)列的事件

當(dāng)有已超時(shí)的 timer,執(zhí)行它的回調(diào)函數(shù)

even loop將同步執(zhí)行poll隊(duì)列里的回調(diào),直到隊(duì)列為空或執(zhí)行的回調(diào)達(dá)到系統(tǒng)上限(上限具體多少未詳),接下來even loop會(huì)去檢查有無預(yù)設(shè)的setImmediate(),分兩種情況:

若有預(yù)設(shè)的setImmediate(), event loop將結(jié)束poll階段進(jìn)入check階段,并執(zhí)行check階段的任務(wù)隊(duì)列

若沒有預(yù)設(shè)的setImmediate(),event loop將阻塞在該階段等待

注意一個(gè)細(xì)節(jié),沒有setImmediate()會(huì)導(dǎo)致event loop阻塞在poll階段,這樣之前設(shè)置的timer豈不是執(zhí)行不了了?所以咧,在poll階段event loop會(huì)有一個(gè)檢查機(jī)制,檢查timer隊(duì)列是否為空,如果timer隊(duì)列非空,event loop就開始下一輪事件循環(huán),即重新進(jìn)入到timer階段。

check 階段

setImmediate()的回調(diào)會(huì)被加入check隊(duì)列中, 從event loop的階段圖可以知道,check階段的執(zhí)行順序在poll階段之后。

小結(jié)

event loop 的每個(gè)階段都有一個(gè)任務(wù)隊(duì)列

當(dāng) event loop 到達(dá)某個(gè)階段時(shí),將執(zhí)行該階段的任務(wù)隊(duì)列,直到隊(duì)列清空或執(zhí)行的回調(diào)達(dá)到系統(tǒng)上限后,才會(huì)轉(zhuǎn)入下一個(gè)階段

當(dāng)所有階段被順序執(zhí)行一次后,稱 event loop 完成了一個(gè) tick

講得好有道理,可是沒有demo我還是理解不全啊,憋急,now!

const fs = require('fs')fs.readFile('test.txt', () => {
  console.log('readFile')
  setTimeout(() => {
    console.log('timeout')
  }, 0)
  setImmediate(() => {
    console.log('immediate')
  })
  })

執(zhí)行結(jié)果應(yīng)該都沒有疑問了

readFile
immediate
timeout

Node.js 與瀏覽器的 Event Loop 差異

回顧上一篇,瀏覽器環(huán)境下,microtask的任務(wù)隊(duì)列是每個(gè)macrotask執(zhí)行完之后執(zhí)行。

Node.js中事件循環(huán)機(jī)制的示例分析

而在Node.js中,microtask會(huì)在事件循環(huán)的各個(gè)階段之間執(zhí)行,也就是一個(gè)階段執(zhí)行完畢,就會(huì)去執(zhí)行microtask隊(duì)列的任務(wù)。

Node.js中事件循環(huán)機(jī)制的示例分析

demo回顧

回顧文章最開始的demo,全局腳本(main())執(zhí)行,將2個(gè)timer依次放入timer隊(duì)列,main()執(zhí)行完畢,調(diào)用??臻e,任務(wù)隊(duì)列開始執(zhí)行;

Node.js中事件循環(huán)機(jī)制的示例分析

首先進(jìn)入timers階段,執(zhí)行timer1的回調(diào)函數(shù),打印timer1,并將promise1.then回調(diào)放入microtask隊(duì)列,同樣的步驟執(zhí)行timer2,打印timer2;

至此,timer階段執(zhí)行結(jié)束,event loop進(jìn)入下一個(gè)階段之前,執(zhí)行microtask隊(duì)列的所有任務(wù),依次打印promise1、promise2。

對(duì)比瀏覽器端的處理過程:

Node.js中事件循環(huán)機(jī)制的示例分析

process.nextTick() VS setImmediate()

In essence, the names should be swapped. process.nextTick() fires more immediately than setImmediate()

來自官方文檔有意思的一句話,從語(yǔ)義角度看,setImmediate() 應(yīng)該比 process.nextTick() 先執(zhí)行才對(duì),而事實(shí)相反,命名是歷史原因也很難再變。

process.nextTick() 會(huì)在各個(gè)事件階段之間執(zhí)行,一旦執(zhí)行,要直到nextTick隊(duì)列被清空,才會(huì)進(jìn)入到下一個(gè)事件階段,所以如果遞歸調(diào)用 process.nextTick(),會(huì)導(dǎo)致出現(xiàn)I/O starving(饑餓)的問題,比如下面例子的readFile已經(jīng)完成,但它的回調(diào)一直無法執(zhí)行:

const fs = require('fs')const starttime = Date.now()let endtime
fs.readFile('text.txt', () => {
  endtime = Date.now()
  console.log('finish reading time: ', endtime - starttime)})let index = 0function handler () {
  if (index++ >= 1000) return
  console.log(`nextTick ${index}`)
  process.nextTick(handler)
  // console.log(`setImmediate ${index}`)
  // setImmediate(handler)}handler()

process.nextTick()的運(yùn)行結(jié)果:

nextTick 1
nextTick 2
......
nextTick 999
nextTick 1000
finish reading time: 170

替換成setImmediate(),運(yùn)行結(jié)果:

setImmediate 1
setImmediate 2
finish reading time: 80
......
setImmediate 999
setImmediate 1000

這是因?yàn)榍短渍{(diào)用的 setImmediate() 回調(diào),被排到了下一次event loop才執(zhí)行,所以不會(huì)出現(xiàn)阻塞。

總結(jié)

1、Node.js 的事件循環(huán)分為6個(gè)階段

2、瀏覽器和Node 環(huán)境下,microtask 任務(wù)隊(duì)列的執(zhí)行時(shí)機(jī)不同

Node.js中,microtask 在事件循環(huán)的各個(gè)階段之間執(zhí)行

瀏覽器端,microtask 在事件循環(huán)的 macrotask 執(zhí)行完之后執(zhí)行

3、遞歸的調(diào)用process.nextTick()會(huì)導(dǎo)致I/O starving,官方推薦使用setImmediate()

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Node.js中事件循環(huán)機(jī)制的示例分析”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián)建站,關(guān)注創(chuàng)新互聯(lián)網(wǎng)站制作公司行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!


新聞名稱:Node.js中事件循環(huán)機(jī)制的示例分析-創(chuàng)新互聯(lián)
鏈接地址:http://weahome.cn/article/dhpjjd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部