亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

事件循環(huán)

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

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

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

總結(jié)來說:

首先會運行主線程的同步代碼,每一行同步代碼都會被壓入執(zhí)行棧,每一行異步代碼會壓入異步API中(如:定時器線程、ajax線程等;),在執(zhí)行棧沒有要執(zhí)行的代碼時,也就是我們當(dāng)前主線程沒有同步代碼了,任務(wù)隊列會從我們的異步任務(wù)微任務(wù)隊列中取一個微任務(wù)放到我們的任務(wù)隊列中進行執(zhí)行,將它的回調(diào)函數(shù)進而再次放到執(zhí)行棧中進行執(zhí)行,當(dāng)微任務(wù)隊列為空時,會在宏任務(wù)中取異步任務(wù)加到任務(wù)隊列,進而壓入執(zhí)行棧,執(zhí)行回調(diào)函數(shù),然后繼續(xù)在該宏任務(wù)中查找同步、異步任務(wù),一次循環(huán),完成了一個事件循環(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,隨后為定時器setTimeout,為異步任務(wù),將代碼放到異步對列中等待執(zhí)行,隨后執(zhí)行promise中的代碼,我們要清楚promise是同步執(zhí)行,它的回調(diào)是異步執(zhí)行,所有打印Promise,將res(‘PromiseRes')放到異步對列中等待執(zhí)行,這個時候又遇到了同步代碼,打印2,當(dāng)前主線程的同步代碼全部執(zhí)行完畢,并且執(zhí)行棧中沒有要執(zhí)行的同步代碼,這個時候webApi會從異步隊列中去微任務(wù)隊列中的第一個,加入到事件隊列執(zhí)行,將返回的回調(diào)函數(shù)壓入到執(zhí)行棧中執(zhí)行,打印PromiseRes,隨后微任務(wù)執(zhí)行完畢,已經(jīng)沒有微任務(wù),現(xiàn)在就需要從宏任務(wù)隊列中取宏任務(wù)定時器,加入到任務(wù)隊列中,將回調(diào)函數(shù)壓入到執(zhí)行棧中執(zhí)行,打印setTimeout。

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

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

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

六個階段

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

在這里插入圖片描述

  • timers階段:用來執(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時間,適當(dāng)?shù)臈l件下node將阻塞在這里;
  • check階段:執(zhí)行setImmediate()的回調(diào);
  • close callbacks 階段:執(zhí)行socket的close時間回調(diào)

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

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

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

(1) setTimeout 和 setImmediate

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

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

這兩個執(zhí)行的時機可前可后:
例子1:

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

在這里插入圖片描述

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

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

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

在這里插入圖片描述

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

const fs = require('fs');

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

在這里插入圖片描述

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

(2) process.nextTick

這個函數(shù)其實是獨立于 Event Loop 之外的,它有一個自己的隊列,當(dāng)每個階段完成后,如果存在 nextTick 隊列,就會清空隊列中的所有回調(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')
    //會等待await執(zhí)行完 但是不會向下執(zhí)行 因為下面輸入微任務(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')

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

例子:

async function async1() {
    console.log('2')
    //會等待await執(zhí)行完 但是不會向下執(zhí)行 因為下面輸入微任務(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

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

相關(guān)文章

最新評論