成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

瀏覽器和Node不同的事件循環(huán)(Event Loop)

haitiancoder / 1103人閱讀

摘要:瀏覽器中與中事件循環(huán)與執(zhí)行機(jī)制不同,不可混為一談。瀏覽器環(huán)境執(zhí)行為單線程不考慮,所有代碼皆在執(zhí)行線程調(diào)用棧完成執(zhí)行。參考文章強(qiáng)烈推薦不要混淆和瀏覽器中的強(qiáng)烈推薦中的模塊強(qiáng)烈推薦理解事件循環(huán)一淺析定時(shí)器詳解

注意
在 node 11 版本中,node 下 Event Loop 已經(jīng)與瀏覽器趨于相同。
在 node 11 版本中,node 下 Event Loop 已經(jīng)與瀏覽器趨于相同。
在 node 11 版本中,node 下 Event Loop 已經(jīng)與瀏覽器趨于相同。
背景

Event Loop也是js老生常談的一個(gè)話題了。2月底看了阮一峰老師的《Node定時(shí)器詳解》一文后,發(fā)現(xiàn)無法完全對標(biāo)之前看過的js事件循環(huán)執(zhí)行機(jī)制,又查閱了一些其他資料,記為筆記,感覺不妥,總結(jié)成文。

瀏覽器中與node中事件循環(huán)與執(zhí)行機(jī)制不同,不可混為一談。
瀏覽器的Event loop是在HTML5中定義的規(guī)范,而node中則由libuv庫實(shí)現(xiàn)。同時(shí)閱讀《深入淺出nodeJs》一書時(shí)發(fā)現(xiàn)比較當(dāng)時(shí)node機(jī)制已有不同,所以本文node部分針對為此文發(fā)布時(shí)版本。強(qiáng)烈推薦讀下參考鏈接中的前三篇。

瀏覽器環(huán)境

js執(zhí)行為單線程(不考慮web worker),所有代碼皆在執(zhí)行線程調(diào)用棧完成執(zhí)行。當(dāng)執(zhí)行線程任務(wù)清空后才會去輪詢?nèi)∪蝿?wù)隊(duì)列中任務(wù)。

任務(wù)隊(duì)列

異步任務(wù)分為task(宏任務(wù),也可稱為macroTask)和microtask(微任務(wù))兩類。
當(dāng)滿足執(zhí)行條件時(shí),task和microtask會被放入各自的隊(duì)列中等待放入執(zhí)行線程執(zhí)行,我們把這兩個(gè)隊(duì)列稱為Task Queue(也叫Macrotask Queue)和Microtask Queue。

task:script中代碼、setTimeout、setInterval、I/O、UI render。

microtask: promise、Object.observe、MutationObserver。

具體過程

執(zhí)行完主執(zhí)行線程中的任務(wù)。

取出Microtask Queue中任務(wù)執(zhí)行直到清空。

取出Macrotask Queue中一個(gè)任務(wù)執(zhí)行。

取出Microtask Queue中任務(wù)執(zhí)行直到清空。

重復(fù)3和4。

即為同步完成,一個(gè)宏任務(wù),所有微任務(wù),一個(gè)宏任務(wù),所有微任務(wù)......

注意

在瀏覽器頁面中可以認(rèn)為初始執(zhí)行線程中沒有代碼,每一個(gè)script標(biāo)簽中的代碼是一個(gè)獨(dú)立的task,即會執(zhí)行完前面的script中創(chuàng)建的microtask再執(zhí)行后面的script中的同步代碼。

如果microtask一直被添加,則會繼續(xù)執(zhí)行microtask,“卡死”macrotask。

部分版本瀏覽器有執(zhí)行順序與上述不符的情況,可能是不符合標(biāo)準(zhǔn)或js與html部分標(biāo)準(zhǔn)沖突??砷喿x參考文章中第一篇。

new Promise((resolve, reject) =>{console.log(‘同步’);resolve()}).then(() => {console.log("異步")}),即promisethencatch才是microtask,本身的內(nèi)部代碼不是。

個(gè)別瀏覽器獨(dú)有API未列出。

偽代碼
while (true) {
  宏任務(wù)隊(duì)列.shift()
  微任務(wù)隊(duì)列全部任務(wù)()
}
node環(huán)境

js執(zhí)行為單線程,所有代碼皆在執(zhí)行線程調(diào)用棧完成執(zhí)行。當(dāng)執(zhí)行線程任務(wù)清空后才會去輪詢?nèi)∪蝿?wù)隊(duì)列中任務(wù)。

循環(huán)階段

在node中事件每一輪循環(huán)按照順序分為6個(gè)階段,來自libuv的實(shí)現(xiàn):

timers:執(zhí)行滿足條件的setTimeout、setInterval回調(diào)。

I/O callbacks:是否有已完成的I/O操作的回調(diào)函數(shù),來自上一輪的poll殘留。

idle,prepare:可忽略

poll:等待還沒完成的I/O事件,會因timers和超時(shí)時(shí)間等結(jié)束等待。

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

close callbacks:關(guān)閉所有的closing handles,一些onclose事件。

執(zhí)行機(jī)制 幾個(gè)隊(duì)列

除上述循環(huán)階段中的任務(wù)類型,我們還剩下瀏覽器和node共有的microtask和node獨(dú)有的process.nextTick,我們稱之為Microtask Queue和NextTick Queue。

我們把循環(huán)中的幾個(gè)階段的執(zhí)行隊(duì)列也分別稱為Timers Queue、I/O Queue、Check Queue、Close Queue。

循環(huán)之前

在進(jìn)入第一次循環(huán)之前,會先進(jìn)行如下操作:

同步任務(wù)

發(fā)出異步請求

規(guī)劃定時(shí)器生效的時(shí)間

執(zhí)行process.nextTick()

開始循環(huán)

按照我們的循環(huán)的6個(gè)階段依次執(zhí)行,每次拿出當(dāng)前階段中的全部任務(wù)執(zhí)行,清空NextTick Queue,清空Microtask Queue。再執(zhí)行下一階段,全部6個(gè)階段執(zhí)行完畢后,進(jìn)入下輪循環(huán)。即:

清空當(dāng)前循環(huán)內(nèi)的Timers Queue,清空NextTick Queue,清空Microtask Queue。

清空當(dāng)前循環(huán)內(nèi)的I/O Queue,清空NextTick Queue,清空Microtask Queue。

清空當(dāng)前循環(huán)內(nèi)的Check Queu,清空NextTick Queue,清空Microtask Queue。

清空當(dāng)前循環(huán)內(nèi)的Close Queu,清空NextTick Queue,清空Microtask Queue。

進(jìn)入下輪循環(huán)。

可以看出,nextTick優(yōu)先級比promise等microtask高。setTimeoutsetInterval優(yōu)先級比setImmediate高。

注意

如果在timers階段執(zhí)行時(shí)創(chuàng)建了setImmediate則會在此輪循環(huán)的check階段執(zhí)行,如果在timers階段創(chuàng)建了setTimeout,由于timers已取出完畢,則會進(jìn)入下輪循環(huán),check階段創(chuàng)建timers任務(wù)同理。

setTimeout優(yōu)先級比setImmediate高,但是由于setTimeout(fn,0)的真正延遲不可能完全為0秒,可能出現(xiàn)先創(chuàng)建的setTimeout(fn,0)而比setImmediate的回調(diào)后執(zhí)行的情況。

偽代碼
while (true) {
  loop.forEach((階段) => {
    階段全部任務(wù)()
    nextTick全部任務(wù)()
    microTask全部任務(wù)()
  })
  loop = loop.next
}
測試代碼
function sleep(time) {
  let startTime = new Date()
  while (new Date() - startTime < time) {}
  console.log("1s over")
}
setTimeout(() => {
  console.log("setTimeout - 1")
  setTimeout(() => {
      console.log("setTimeout - 1 - 1")
      sleep(1000)
  })
  new Promise(resolve => resolve()).then(() => {
      console.log("setTimeout - 1 - then")
      new Promise(resolve => resolve()).then(() => {
          console.log("setTimeout - 1 - then - then")
      })
  })
  sleep(1000)
})

setTimeout(() => {
  console.log("setTimeout - 2")
  setTimeout(() => {
      console.log("setTimeout - 2 - 1")
      sleep(1000)
  })
  new Promise(resolve => resolve()).then(() => {
      console.log("setTimeout - 2 - then")
      new Promise(resolve => resolve()).then(() => {
          console.log("setTimeout - 2 - then - then")
      })
  })
  sleep(1000)
})

瀏覽器輸出:

setTimeout - 1 //1為單個(gè)task
1s over
setTimeout - 1 - then
setTimeout - 1 - then - then 
setTimeout - 2 //2為單個(gè)task
1s over
setTimeout - 2 - then
setTimeout - 2 - then - then
setTimeout - 1 - 1
1s over
setTimeout - 2 - 1
1s over

node輸出:

setTimeout - 1 
1s over
setTimeout - 2 //1、2為單階段task
1s over
setTimeout - 1 - then
setTimeout - 2 - then
setTimeout - 1 - then - then
setTimeout - 2 - then - then
setTimeout - 1 - 1
1s over
setTimeout - 2 - 1
1s over

由此也可看出事件循環(huán)在瀏覽器和node中的不同。

參考文章

Tasks, microtasks, queues and schedules 強(qiáng)烈推薦

不要混淆nodejs和瀏覽器中的event loop 強(qiáng)烈推薦

node中的Event模塊 強(qiáng)烈推薦

理解事件循環(huán)一(淺析)

Node 定時(shí)器詳解

???

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/93347.html

相關(guān)文章

  • 一篇文章教會你Event loop——覽器Node

    摘要:如果沒到毫秒,那么階段就會跳過,進(jìn)入階段,先執(zhí)行的回調(diào)函數(shù)。參考文檔什么是瀏覽器的事件循環(huán)不要混淆和瀏覽器中的定時(shí)器詳解瀏覽器和不同的事件循環(huán)深入理解事件循環(huán)機(jī)制篇中的執(zhí)行機(jī)制 最近對Event loop比較感興趣,所以了解了一下。但是發(fā)現(xiàn)整個(gè)Event loop盡管有很多篇文章,但是沒有一篇可以看完就對它所有內(nèi)容都了解的文章。大部分的文章都只闡述了瀏覽器或者Node二者之一,沒有對比...

    Leck1e 評論0 收藏0
  • 覽器Node事件循環(huán)(Event Loop)有何區(qū)別?

    摘要:事件觸發(fā)線程主要負(fù)責(zé)將準(zhǔn)備好的事件交給引擎線程執(zhí)行。它將不同的任務(wù)分配給不同的線程,形成一個(gè)事件循環(huán),以異步的方式將任務(wù)的執(zhí)行結(jié)果返回給引擎。 Fundebug經(jīng)作者浪里行舟授權(quán)首發(fā),未經(jīng)同意請勿轉(zhuǎn)載。 前言 本文我們將會介紹 JS 實(shí)現(xiàn)異步的原理,并且了解了在瀏覽器和 Node 中 Event Loop 其實(shí)是不相同的。 一、線程與進(jìn)程 1. 概念 我們經(jīng)常說 JS 是單線程執(zhí)行的,...

    TANKING 評論0 收藏0
  • FE.ES-理解Event Loop

    摘要:新加了一個(gè)微任務(wù)和一個(gè)宏任務(wù)在當(dāng)前執(zhí)行棧的尾部下一次之前觸發(fā)回調(diào)函數(shù)。階段這個(gè)階段主要執(zhí)行一些系統(tǒng)操作帶來的回調(diào)函數(shù),如錯(cuò)誤,如果嘗試鏈接時(shí)出現(xiàn)錯(cuò)誤,一些會把這個(gè)錯(cuò)誤報(bào)告給。 JavaScript引擎又稱為JavaScript解釋器,是JavaScript解釋為機(jī)器碼的工具,分別運(yùn)行在瀏覽器和Node中。而根據(jù)上下文的不同,Event loop也有不同的實(shí)現(xiàn):其中Node使用了libu...

    longshengwang 評論0 收藏0
  • Event Loop - JS執(zhí)行機(jī)制

    摘要:心塞塞根據(jù)規(guī)范,事件循環(huán)是通過任務(wù)隊(duì)列的機(jī)制來進(jìn)行協(xié)調(diào)的。等便是任務(wù)源,而進(jìn)入任務(wù)隊(duì)列的是他們指定的具體執(zhí)行任務(wù)回調(diào)函數(shù)。然后當(dāng)前本輪的結(jié)束,主線程可以繼續(xù)取下一個(gè)執(zhí)行。 依然是:經(jīng)濟(jì)基礎(chǔ)決定上層建筑。 說明 首先,旨在搞清常用的同步異步執(zhí)行機(jī)制 其次,暫時(shí)不討論node.js的Event Loop執(zhí)行機(jī)制,以下關(guān)于瀏覽器的Event Loop執(zhí)行機(jī)制 最后,借鑒了很多前輩的研究文...

    muddyway 評論0 收藏0
  • JS與Node.js中事件循環(huán)

    摘要:的單線程,與它的用途有關(guān)。特點(diǎn)的顯著特點(diǎn)異步機(jī)制事件驅(qū)動(dòng)。隊(duì)列的讀取輪詢線程,事件的消費(fèi)者,的主角。它將不同的任務(wù)分配給不同的線程,形成一個(gè)事件循環(huán),以異步的方式將任務(wù)的執(zhí)行結(jié)果返回給引擎。 這兩天跟同事同事討論遇到的一個(gè)問題,js中的event loop,引出了chrome與node中運(yùn)行具有setTimeout和Promise的程序時(shí)候執(zhí)行結(jié)果不一樣的問題,從而引出了Nodejs的...

    abson 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<