欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

node事件循環(huán)中事件執(zhí)行的順序

 更新時(shí)間:2021年08月30日 11:28:23   作者:六卿  
在瀏覽器環(huán)境下我們的js有一套自己的事件循環(huán),同樣在node環(huán)境下也有一套類似的事件循環(huán)。本文就詳細(xì)的來(lái)介紹一下,感興趣的可以了解一下

事件循環(huán)

在瀏覽器環(huán)境下我們的js有一套自己的事件循環(huán),同樣在node環(huán)境下也有一套類似的事件循環(huán)。

瀏覽器環(huán)境事件循環(huán)

首先,我們先來(lái)回顧一下在瀏覽器的事件循環(huán):

總結(jié)來(lái)說(shuō):

首先會(huì)運(yùn)行主線程的同步代碼,每一行同步代碼都會(huì)被壓入執(zhí)行棧,每一行異步代碼會(huì)壓入異步API中(如:定時(shí)器線程、ajax線程等;),在執(zhí)行棧沒(méi)有要執(zhí)行的代碼時(shí),也就是我們當(dāng)前主線程沒(méi)有同步代碼了,任務(wù)隊(duì)列會(huì)從我們的異步任務(wù)微任務(wù)隊(duì)列中取一個(gè)微任務(wù)放到我們的任務(wù)隊(duì)列中進(jìn)行執(zhí)行,將它的回調(diào)函數(shù)進(jìn)而再次放到執(zhí)行棧中進(jìn)行執(zhí)行,當(dāng)微任務(wù)隊(duì)列為空時(shí),會(huì)在宏任務(wù)中取異步任務(wù)加到任務(wù)隊(duì)列,進(jìn)而壓入執(zhí)行棧,執(zhí)行回調(diào)函數(shù),然后繼續(xù)在該宏任務(wù)中查找同步、異步任務(wù),一次循環(huán),完成了一個(gè)事件循環(huán)(事件輪詢)

瀏覽器環(huán)境下的例子:

例子:

        console.log("1");
        setTimeout(() => {
            console.log("setTimeout");
        }, 1);
        new Promise((res, rej) => {
            console.log("Promise");
            res('PromiseRes')
        }).then(val => {
            console.log(val);
        })
        console.log("2");

分析:
首先執(zhí)行棧找到第一行的同步代碼,直接扔到執(zhí)行棧中執(zhí)行,打印1,隨后為定時(shí)器setTimeout,為異步任務(wù),將代碼放到異步對(duì)列中等待執(zhí)行,隨后執(zhí)行promise中的代碼,我們要清楚promise是同步執(zhí)行,它的回調(diào)是異步執(zhí)行,所有打印Promise,將res(‘PromiseRes')放到異步對(duì)列中等待執(zhí)行,這個(gè)時(shí)候又遇到了同步代碼,打印2,當(dāng)前主線程的同步代碼全部執(zhí)行完畢,并且執(zhí)行棧中沒(méi)有要執(zhí)行的同步代碼,這個(gè)時(shí)候webApi會(huì)從異步隊(duì)列中去微任務(wù)隊(duì)列中的第一個(gè),加入到事件隊(duì)列執(zhí)行,將返回的回調(diào)函數(shù)壓入到執(zhí)行棧中執(zhí)行,打印PromiseRes,隨后微任務(wù)執(zhí)行完畢,已經(jīng)沒(méi)有微任務(wù),現(xiàn)在就需要從宏任務(wù)隊(duì)列中取宏任務(wù)定時(shí)器,加入到任務(wù)隊(duì)列中,將回調(diào)函數(shù)壓入到執(zhí)行棧中執(zhí)行,打印setTimeout。

node環(huán)境事件循環(huán)

在node中事件循環(huán)主要分為六個(gè)階段來(lái)實(shí)現(xiàn):

外部數(shù)據(jù)輸入–》輪詢階段–》檢查階段–》關(guān)閉事件回調(diào)階段–》定時(shí)器階段–》I/O回調(diào)階段–》閑置階段–》輪詢階段》…開始循環(huán)

六個(gè)階段

圖片來(lái)自網(wǎng)絡(luò)

在這里插入圖片描述

  • timers階段:用來(lái)執(zhí)行timer(setTimeout,setInterval)的回調(diào);
  • I/O callbacks階段:處理一些上一輪循環(huán)中少數(shù)未執(zhí)行的I/O回調(diào)
  • idle,prepare 階段:僅node內(nèi)部使用,我們用不到;
  • poll階段:獲取新的I/O時(shí)間,適當(dāng)?shù)臈l件下node將阻塞在這里;
  • check階段:執(zhí)行setImmediate()的回調(diào);
  • close callbacks 階段:執(zhí)行socket的close時(shí)間回調(diào)

主要階段
timer:
timers階段會(huì)執(zhí)行setTimeout和setInterval回調(diào),并且是由poll階段控制的。
同樣,在node中定時(shí)器指定的時(shí)間也不是準(zhǔn)確時(shí)間,只能是盡快執(zhí)行。
poll:
poll這一階段中,系統(tǒng)會(huì)做兩件事情:
1.回到timer階段執(zhí)行回調(diào)
2.執(zhí)行I/O回調(diào)
并且在進(jìn)入該階段時(shí)如果沒(méi)有設(shè)定了timer 的話,會(huì)發(fā)生以下兩件事情

如果 poll 隊(duì)列不為空,會(huì)遍歷回調(diào)隊(duì)列并同步執(zhí)行,直到隊(duì)列為空或者達(dá)到系統(tǒng)限制
如果 poll 隊(duì)列為空時(shí),會(huì)有兩件事發(fā)生
1、如果有 setImmediate 回調(diào)需要執(zhí)行,poll 階段會(huì)停止并且進(jìn)入到 check 階段執(zhí)行回調(diào)
2、如果沒(méi)有 setImmediate 回調(diào)需要執(zhí)行,會(huì)等待回調(diào)被加入到隊(duì)列中并立即執(zhí)行回調(diào),這里同樣會(huì)有個(gè)超時(shí)時(shí)間設(shè)置防止一直等待下去
當(dāng)然設(shè)定了 timer 的話且 poll 隊(duì)列為空,則會(huì)判斷是否有 timer 超時(shí),如果有的話會(huì)回到 timer 階段執(zhí)行回調(diào)。

check階段
setImmediate()的回調(diào)會(huì)被加入 check 隊(duì)列中,從 event loop 的階段圖可以知道,check 階段的執(zhí)行順序在 poll 階段之后,在進(jìn)入check階段執(zhí)勤poll會(huì)檢查有的話到check階段,沒(méi)有的換直接到timer階段。

(1) setTimeout 和 setImmediate

二者非常相似,區(qū)別主要在于調(diào)用時(shí)機(jī)不同。

setImmediate 設(shè)計(jì)在 poll 階段完成時(shí)執(zhí)行,即 check 階段,只有在check階段才會(huì)執(zhí)行;
setTimeout 設(shè)計(jì)在 poll 階段為空閑時(shí),且設(shè)定時(shí)間到達(dá)后執(zhí)行,但它在 timer 階段執(zhí)行,表示當(dāng)前線程沒(méi)有其他可執(zhí)行的同步任務(wù),才會(huì)在timer階段執(zhí)行定時(shí)器。

這兩個(gè)執(zhí)行的時(shí)機(jī)可前可后:
例子1:

// //異步任務(wù)中的宏任務(wù)
setTimeout(() => {
    console.log('===setTimeout===');
},0);
setImmediate(() => {
    console.log('===setImmediate===')
})

在這里插入圖片描述

多次重復(fù)執(zhí)行的結(jié)果會(huì)不同,有一種隨機(jī)的感覺(jué),出現(xiàn)這種情況的原因主要和setTimeout的實(shí)現(xiàn)代碼有關(guān),當(dāng)我們不傳時(shí)間參數(shù)或者設(shè)置為0的時(shí)候,nodejs會(huì)取值為1,即1ms(在瀏覽器端可能取值會(huì)更大一下,不同瀏覽器也各不相同),所以在電腦cpu性能夠強(qiáng),能夠在1ms內(nèi)執(zhí)行到timers phase的情況下,由于時(shí)間延遲不滿足回調(diào)不會(huì)被執(zhí)行,于是只能等到第二輪再執(zhí)行,這樣setInterval就會(huì)先執(zhí)行。
可能由于cpu多次執(zhí)行相同任務(wù)用時(shí)會(huì)有細(xì)微差別,而且在1ms上下浮動(dòng),才會(huì)造成上面的隨機(jī)現(xiàn)象
一般情況下setTimeout為0時(shí)候會(huì)在setImmediate之前執(zhí)行

例子2:
當(dāng)我們傳入的值大于定時(shí)器timer執(zhí)行的回調(diào)時(shí)間的時(shí)候會(huì)直接導(dǎo)致定時(shí)器在下一次事件循環(huán)中執(zhí)行

setTimeout(() => {
    console.log('===setTimeout===');
},10);
setImmediate(() => {
    console.log('===setImmediate===')
})

在這里插入圖片描述

例子3:
當(dāng)我們將上述代碼放入一個(gè)i/o中就會(huì)固定先check再而timer:

const fs = require('fs');

fs.readFile("./any.js", (data) => {
    setTimeout(() => {
        console.log('===setTimeout===');
    },10);
    setImmediate(() => {
        console.log('===setImmediate===')
    })
});

在這里插入圖片描述

在第一輪循環(huán)中讀取文件,在回調(diào)中,會(huì)進(jìn)入check階段進(jìn)而執(zhí)行setImmediate,隨后timer階段執(zhí)行定時(shí)器。
setimmediate 與 settimeout 放入一個(gè) I/O 循環(huán)內(nèi)調(diào)用,則 setImmediate 總是被優(yōu)先調(diào)用

(2) process.nextTick

這個(gè)函數(shù)其實(shí)是獨(dú)立于 Event Loop 之外的,它有一個(gè)自己的隊(duì)列,當(dāng)每個(gè)階段完成后,如果存在 nextTick 隊(duì)列,就會(huì)清空隊(duì)列中的所有回調(diào)函數(shù),并且優(yōu)先于其他 microtask 執(zhí)行。

例子1:

setTimeout(() => {
 console.log('timer1')
 Promise.resolve().then(function() {
   console.log('promise1')
 })
}, 0)
process.nextTick(() => {
 console.log('nextTick')
 process.nextTick(() => {
   console.log('nextTick')
   process.nextTick(() => {
     console.log('nextTick')
     process.nextTick(() => {
       console.log('nextTick')
     })
   })
 })
})
// nextTick=>nextTick=>nextTick=>nextTick=>timer1=>promise1

例子2:

const fs = require('fs');

fs.readFile("./any.js", (data) => {
    process.nextTick(()=>console.log('process===2'))
    setTimeout(() => {
        console.log('===setTimeout===');
    },10);
    setImmediate(() => {
        console.log('===setImmediate===')
    })
});
process.nextTick(()=>console.log('process===1'))

在這里插入圖片描述

練習(xí)例子

async function async1() {
    console.log('2')
    //會(huì)等待await執(zhí)行完 但是不會(huì)向下執(zhí)行 因?yàn)橄旅孑斎胛⑷蝿?wù)
    await async2()
    console.log('9')
  }
   
   function async2() {
    console.log('3')
  }
   
  console.log('1')
   
  setTimeout(function () {
    console.log('11')
  }, 0)
   
  setTimeout(function () {
    console.log('13')
  }, 300)
   
  setImmediate(() => console.log('12'));
   
  process.nextTick(() => console.log('7'));
   
  async1();
   
  process.nextTick(() => console.log('8'));
   
  new Promise(function (resolve) {
    console.log('4')
    resolve();
    console.log('5')
  }).then(function () {
    console.log('10')
  })
   
  console.log('6')

分析:
上面的循序就是序號(hào)的順序;
首先打印1:
前面都是兩個(gè)函數(shù)聲明,所有直接打印1,這行同步代碼;
打印2:
打印完1后,都是異步代碼,加入異步任務(wù)隊(duì)列,直接到async1函數(shù)調(diào)用,在這個(gè)函數(shù)中打印2;
打印3:
async1這個(gè)函數(shù)是個(gè)async await函數(shù),所有也是一個(gè)變相的同步操縱等待async2函數(shù)執(zhí)行,async2執(zhí)行后并不會(huì)直接打印9,原因await接受的是一個(gè)promise的then操作,所以后面屬于一個(gè)promise的回調(diào)操作屬于微任務(wù),加入微任務(wù)隊(duì)列;
打印4:
process.nextTick為微任務(wù),所以會(huì)繼續(xù)執(zhí)行promise,打印4;
打印5:
resolve()的回調(diào)不會(huì)立即執(zhí)行屬于微任務(wù),加入微任務(wù)隊(duì)列,所以打印5;
打印6:
最后一個(gè)主線程的同步代碼,打印6;
打印7、8:
process.nextTick優(yōu)先級(jí)高于其他定時(shí)器,所以會(huì)直接執(zhí)行回調(diào)函數(shù)打印7、8;
打印9、10:
這個(gè)時(shí)候需要執(zhí)行微任務(wù)隊(duì)列中的微任務(wù),目前有兩個(gè)9和10,按照先后循序,先打印9后打印10;
打印11、12:
setTimeout為0秒比setImmediate執(zhí)行早,按照先后循序,先打印11后打印12;
打印13:
setTimeout為300ms的函數(shù),打印13;

例子:

async function async1() {
    console.log('2')
    //會(huì)等待await執(zhí)行完 但是不會(huì)向下執(zhí)行 因?yàn)橄旅孑斎胛⑷蝿?wù)
    await async2()
    console.log('9')
  }
   
   function async2() {
    console.log('3')
  }
   
  console.log('1')
   
  setTimeout(function () {
    console.log('11')
    setTimeout(() => {
        console.log('11-1');
    },100);
    setImmediate(() => {
        console.log('11-2')
    })
  }, 0)
   
  setTimeout(function () {
    console.log('13')
    setTimeout(() => {
        console.log('15');
    },10);
    setImmediate(() => {
        console.log('14')
    })
  }, 300)
  setImmediate(() => console.log('12'));
  process.nextTick(() => console.log('7'));
  async1();
   
  process.nextTick(() => console.log('8'));
   
  new Promise(function (resolve) {
    console.log('4')
    resolve();
    console.log('5')
  }).then(function () {
    console.log('10')
  })
   
  console.log('6')

總結(jié):

到此這篇關(guān)于node事件循環(huán)中事件執(zhí)行的順序的文章就介紹到這了,更多相關(guān)node 事件執(zhí)行的順序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

參考:https://www.cnblogs.com/everlose/p/12846375.html

相關(guān)文章

最新評(píng)論