瀏覽器與Node的事件循環(huán)(Event Loop)有何區(qū)別?

前言

本文我們將會(huì)介紹 JS 實(shí)現(xiàn)異步的原理,并且了解了在瀏覽器和 Node 中 Event Loop 其實(shí)是不相同的诈嘿。

一、線程與進(jìn)程

1.概念

我們經(jīng)常說JS 是單線程執(zhí)行的偎球,指的是一個(gè)進(jìn)程里只有一個(gè)主線程瞳秽,那到底什么是線程?什么是進(jìn)程啼县?

官方的說法是:進(jìn)程是 CPU資源分配的最小單位材原;線程是 CPU調(diào)度的最小單位。這兩句話并不好理解季眷,我們先來看張圖:

image

  • 進(jìn)程好比圖中的工廠余蟹,有單獨(dú)的專屬自己的工廠資源。
  • 線程好比圖中的工人子刮,多個(gè)工人在一個(gè)工廠中協(xié)作工作威酒,工廠與工人是 1:n的關(guān)系。也就是說一個(gè)進(jìn)程由一個(gè)或多個(gè)線程組成挺峡,線程是一個(gè)進(jìn)程中代碼的不同執(zhí)行路線兼搏;
  • 工廠的空間是工人們共享的,這象征一個(gè)進(jìn)程的內(nèi)存空間是共享的沙郭,每個(gè)線程都可用這些共享內(nèi)存佛呻。
  • 多個(gè)工廠之間獨(dú)立存在。

2.多進(jìn)程與多線程

  • 多進(jìn)程:在同一個(gè)時(shí)間里病线,同一個(gè)計(jì)算機(jī)系統(tǒng)中如果允許兩個(gè)或兩個(gè)以上的進(jìn)程處于運(yùn)行狀態(tài)吓著。多進(jìn)程帶來的好處是明顯的,比如你可以聽歌的同時(shí)送挑,打開編輯器敲代碼绑莺,編輯器和聽歌軟件的進(jìn)程之間絲毫不會(huì)相互干擾。
  • 多線程:程序中包含多個(gè)執(zhí)行流惕耕,即在一個(gè)程序中可以同時(shí)運(yùn)行多個(gè)不同的線程來執(zhí)行不同的任務(wù)纺裁,也就是說允許單個(gè)程序創(chuàng)建多個(gè)并行執(zhí)行的線程來完成各自的任務(wù)。

以Chrome瀏覽器中為例司澎,當(dāng)你打開一個(gè) Tab 頁時(shí)欺缘,其實(shí)就是創(chuàng)建了一個(gè)進(jìn)程,一個(gè)進(jìn)程中可以有多個(gè)線程(下文會(huì)詳細(xì)介紹)挤安,比如渲染線程谚殊、JS 引擎線程、HTTP 請(qǐng)求線程等等蛤铜。當(dāng)你發(fā)起一個(gè)請(qǐng)求時(shí)嫩絮,其實(shí)就是創(chuàng)建了一個(gè)線程丛肢,當(dāng)請(qǐng)求結(jié)束后,該線程可能就會(huì)被銷毀剿干。

二蜂怎、瀏覽器內(nèi)核

簡單來說瀏覽器內(nèi)核是通過取得頁面內(nèi)容、整理信息(應(yīng)用CSS)置尔、計(jì)算和組合最終輸出可視化的圖像結(jié)果派敷,通常也被稱為渲染引擎。

瀏覽器內(nèi)核是多線程撰洗,在內(nèi)核控制下各線程相互配合以保持同步篮愉,一個(gè)瀏覽器通常由以下常駐線程組成:

  • GUI 渲染線程
  • JavaScript引擎線程
  • 定時(shí)觸發(fā)器線程
  • 事件觸發(fā)線程
  • 異步http請(qǐng)求線程

1.GUI渲染線程

  • 主要負(fù)責(zé)頁面的渲染,解析HTML差导、CSS试躏,構(gòu)建DOM樹,布局和繪制等设褐。
  • 當(dāng)界面需要重繪或者由于某種操作引發(fā)回流時(shí)颠蕴,將執(zhí)行該線程。
  • 該線程與JS引擎線程互斥助析,當(dāng)執(zhí)行JS引擎線程時(shí)犀被,GUI渲染會(huì)被掛起,當(dāng)任務(wù)隊(duì)列空閑時(shí)外冀,主線程才會(huì)去執(zhí)行GUI渲染寡键。

2.JS引擎線程

  • 該線程當(dāng)然是主要負(fù)責(zé)處理 JavaScript腳本,執(zhí)行代碼雪隧。
  • 也是主要負(fù)責(zé)執(zhí)行準(zhǔn)備好待執(zhí)行的事件西轩,即定時(shí)器計(jì)數(shù)結(jié)束,或者異步請(qǐng)求成功并正確返回時(shí)脑沿,將依次進(jìn)入任務(wù)隊(duì)列藕畔,等待 JS引擎線程的執(zhí)行。
  • 當(dāng)然庄拇,該線程與 GUI渲染線程互斥注服,當(dāng) JS引擎線程執(zhí)行 JavaScript腳本時(shí)間過長,將導(dǎo)致頁面渲染的阻塞措近。

3.定時(shí)器觸發(fā)線程

  • 負(fù)責(zé)執(zhí)行異步定時(shí)器一類的函數(shù)的線程溶弟,如: setTimeout,setInterval熄诡。
  • 主線程依次執(zhí)行代碼時(shí)可很,遇到定時(shí)器诗力,會(huì)將定時(shí)器交給該線程處理凰浮,當(dāng)計(jì)數(shù)完畢后我抠,事件觸發(fā)線程會(huì)將計(jì)數(shù)完畢后的事件加入到任務(wù)隊(duì)列的尾部,等待JS引擎線程執(zhí)行袜茧。

4.事件觸發(fā)線程

  • 主要負(fù)責(zé)將準(zhǔn)備好的事件交給 JS引擎線程執(zhí)行菜拓。

比如 setTimeout定時(shí)器計(jì)數(shù)結(jié)束, ajax等異步請(qǐng)求成功并觸發(fā)回調(diào)函數(shù)笛厦,或者用戶觸發(fā)點(diǎn)擊事件時(shí)纳鼎,該線程會(huì)將整裝待發(fā)的事件依次加入到任務(wù)隊(duì)列的隊(duì)尾,等待 JS引擎線程的執(zhí)行裳凸。

5.異步http請(qǐng)求線程

  • 負(fù)責(zé)執(zhí)行異步請(qǐng)求一類的函數(shù)的線程贱鄙,如: Promise,axios姨谷,ajax等逗宁。
  • 主線程依次執(zhí)行代碼時(shí),遇到異步請(qǐng)求梦湘,會(huì)將函數(shù)交給該線程處理瞎颗,當(dāng)監(jiān)聽到狀態(tài)碼變更,如果有回調(diào)函數(shù)捌议,事件觸發(fā)線程會(huì)將回調(diào)函數(shù)加入到任務(wù)隊(duì)列的尾部哼拔,等待JS引擎線程執(zhí)行。

三瓣颅、瀏覽器中的 Event Loop

1.Micro-Task 與 Macro-Task

瀏覽器端事件循環(huán)中的異步隊(duì)列有兩種:macro(宏任務(wù))隊(duì)列和 micro(微任務(wù))隊(duì)列倦逐。宏任務(wù)隊(duì)列可以有多個(gè),微任務(wù)隊(duì)列只有一個(gè)宫补。

  • 常見的 macro-task 比如:setTimeout僻孝、setInterval、script(整體代碼)守谓、 I/O 操作穿铆、UI 渲染等。
  • 常見的 micro-task 比如: new Promise().then(回調(diào))斋荞、MutationObserver(html5新特性) 等荞雏。

2.Event Loop 過程解析

一個(gè)完整的 Event Loop 過程,可以概括為以下階段:

image
  • 一開始執(zhí)行椘侥穑空,我們可以把執(zhí)行棧認(rèn)為是一個(gè)存儲(chǔ)函數(shù)調(diào)用的棧結(jié)構(gòu)凤优,遵循先進(jìn)后出的原則。micro 隊(duì)列空蜈彼,macro 隊(duì)列里有且只有一個(gè) script 腳本(整體代碼)筑辨。

  • 全局上下文(script 標(biāo)簽)被推入執(zhí)行棧,同步代碼執(zhí)行幸逆。在執(zhí)行的過程中棍辕,會(huì)判斷是同步任務(wù)還是異步任務(wù)暮现,通過對(duì)一些接口的調(diào)用,可以產(chǎn)生新的 macro-task 與 micro-task楚昭,它們會(huì)分別被推入各自的任務(wù)隊(duì)列里栖袋。同步代碼執(zhí)行完了,script 腳本會(huì)被移出 macro 隊(duì)列抚太,這個(gè)過程本質(zhì)上是隊(duì)列的 macro-task 的執(zhí)行和出隊(duì)的過程塘幅。

  • 上一步我們出隊(duì)的是一個(gè) macro-task,這一步我們處理的是 micro-task尿贫。但需要注意的是:當(dāng) macro-task 出隊(duì)時(shí)电媳,任務(wù)是一個(gè)一個(gè)執(zhí)行的;而 micro-task 出隊(duì)時(shí)庆亡,任務(wù)是一隊(duì)一隊(duì)執(zhí)行的匆背。因此,我們處理 micro 隊(duì)列這一步身冀,會(huì)逐個(gè)執(zhí)行隊(duì)列中的任務(wù)并把它出隊(duì)钝尸,直到隊(duì)列被清空。

  • 執(zhí)行渲染操作搂根,更新界面

  • 檢查是否存在 Web worker 任務(wù)珍促,如果有,則對(duì)其進(jìn)行處理

  • 上述過程循環(huán)往復(fù)剩愧,直到兩個(gè)隊(duì)列都清空

我們總結(jié)一下猪叙,每一次循環(huán)都是一個(gè)這樣的過程:


image

當(dāng)某個(gè)宏任務(wù)執(zhí)行完后,會(huì)查看是否有微任務(wù)隊(duì)列。如果有仁卷,先執(zhí)行微任務(wù)隊(duì)列中的所有任務(wù)穴翩,如果沒有,會(huì)讀取宏任務(wù)隊(duì)列中排在最前的任務(wù)锦积,執(zhí)行宏任務(wù)的過程中芒帕,遇到微任務(wù),依次加入微任務(wù)隊(duì)列丰介。棻丑。空后,再次讀取微任務(wù)隊(duì)列里的任務(wù)哮幢,依次類推带膀。

接下來我們看道例子來介紹上面流程:

Promise.resolve().then(()=>{
  console.log('Promise1')  
  setTimeout(()=>{
    console.log('setTimeout2')
  },0)
})
setTimeout(()=>{
  console.log('setTimeout1')
  Promise.resolve().then(()=>{
    console.log('Promise2')    
  })
},0)

最后輸出結(jié)果是Promise1,setTimeout1橙垢,Promise2垛叨,setTimeout2

  • 一開始執(zhí)行棧的同步任務(wù)(這屬于宏任務(wù))執(zhí)行完畢,會(huì)去查看是否有微任務(wù)隊(duì)列柜某,上題中存在(有且只有一個(gè))嗽元,然后執(zhí)行微任務(wù)隊(duì)列中的所有任務(wù)輸出Promise1敛纲,同時(shí)會(huì)生成一個(gè)宏任務(wù) setTimeout2
  • 然后去查看宏任務(wù)隊(duì)列,宏任務(wù) setTimeout1 在 setTimeout2 之前还棱,先執(zhí)行宏任務(wù) setTimeout1载慈,輸出 setTimeout1
  • 在執(zhí)行宏任務(wù)setTimeout1時(shí)會(huì)生成微任務(wù)Promise2 惭等,放入微任務(wù)隊(duì)列中珍手,接著先去清空微任務(wù)隊(duì)列中的所有任務(wù),輸出 Promise2
  • 清空完微任務(wù)隊(duì)列中的所有任務(wù)后辞做,就又會(huì)去宏任務(wù)隊(duì)列取一個(gè)琳要,這回執(zhí)行的是 setTimeout2

四、Node 中的 Event Loop

1.Node簡介

Node 中的 Event Loop 和瀏覽器中的是完全不相同的東西秤茅。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)(下文會(huì)詳細(xì)介紹)五垮。

image

Node.js的運(yùn)行機(jī)制如下:

  • V8引擎解析JavaScript腳本乍惊。
  • 解析后的代碼,調(diào)用Node API放仗。
  • libuv庫負(fù)責(zé)Node API的執(zhí)行润绎。它將不同的任務(wù)分配給不同的線程,形成一個(gè)Event Loop(事件循環(huán))诞挨,以異步的方式將任務(wù)的執(zhí)行結(jié)果返回給V8引擎莉撇。
  • V8引擎再將結(jié)果返回給用戶。

2.六個(gè)階段

其中l(wèi)ibuv引擎中的事件循環(huán)分為 6 個(gè)階段惶傻,它們會(huì)按照順序反復(fù)運(yùn)行棍郎。每當(dāng)進(jìn)入某一個(gè)階段的時(shí)候,都會(huì)從對(duì)應(yīng)的回調(diào)隊(duì)列中取出函數(shù)去執(zhí)行银室。當(dāng)隊(duì)列為空或者執(zhí)行的回調(diào)函數(shù)數(shù)量到達(dá)系統(tǒng)設(shè)定的閾值坝撑,就會(huì)進(jìn)入下一階段。

image

從上圖中粮揉,大致看出node中的事件循環(huán)的順序:

外部輸入數(shù)據(jù)-->輪詢階段(poll)-->檢查階段(check)-->關(guān)閉事件回調(diào)階段(close callback)-->定時(shí)器檢測階段(timer)-->I/O事件回調(diào)階段(I/O callbacks)-->閑置階段(idle, prepare)-->輪詢階段(按照該順序反復(fù)運(yùn)行)...

  • timers 階段:這個(gè)階段執(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)

注意:上面六個(gè)階段都不包括 process.nextTick()(下文會(huì)介紹)

接下去我們?cè)敿?xì)介紹timerspoll扶认、check這3個(gè)階段侨拦,因?yàn)槿粘i_發(fā)中的絕大部分異步任務(wù)都是在這3個(gè)階段處理的。

(1) timer

timers 階段會(huì)執(zhí)行 setTimeout 和 setInterval 回調(diào)辐宾,并且是由 poll 階段控制的狱从。
同樣膨蛮,在 Node 中定時(shí)器指定的時(shí)間也不是準(zhǔn)確時(shí)間,只能是盡快執(zhí)行季研。

(2) poll

poll 是一個(gè)至關(guān)重要的階段敞葛,這一階段中,系統(tǒng)會(huì)做兩件事情

1.回到 timer 階段執(zhí)行回調(diào)

2.執(zhí)行 I/O 回調(diào)

并且在進(jìn)入該階段時(shí)如果沒有設(shè)定了 timer 的話与涡,會(huì)發(fā)生以下兩件事情

  • 如果 poll 隊(duì)列不為空惹谐,會(huì)遍歷回調(diào)隊(duì)列并同步執(zhí)行,直到隊(duì)列為空或者達(dá)到系統(tǒng)限制
  • 如果 poll 隊(duì)列為空時(shí)驼卖,會(huì)有兩件事發(fā)生
    • 如果有 setImmediate 回調(diào)需要執(zhí)行氨肌,poll 階段會(huì)停止并且進(jìn)入到 check 階段執(zhí)行回調(diào)
    • 如果沒有 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)桥胞。

(3) check階段

setImmediate()的回調(diào)會(huì)被加入check隊(duì)列中恳守,從event loop的階段圖可以知道,check階段的執(zhí)行順序在poll階段之后贩虾。
我們先來看個(gè)例子:

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')
//start=>end=>promise3=>timer1=>timer2=>promise1=>promise2

  • 一開始執(zhí)行棧的同步任務(wù)(這屬于宏任務(wù))執(zhí)行完畢后(依次打印出start end催烘,并將2個(gè)timer依次放入timer隊(duì)列),會(huì)先去執(zhí)行微任務(wù)(這點(diǎn)跟瀏覽器端的一樣),所以打印出promise3
  • 然后進(jìn)入timers階段整胃,執(zhí)行timer1的回調(diào)函數(shù)颗圣,打印timer1,并將promise.then回調(diào)放入microtask隊(duì)列屁使,同樣的步驟執(zhí)行timer2在岂,打印timer2;這點(diǎn)跟瀏覽器端相差比較大蛮寂,timers階段有幾個(gè)setTimeout/setInterval都會(huì)依次執(zhí)行蔽午,并不像瀏覽器端,每執(zhí)行一個(gè)宏任務(wù)后就去執(zhí)行一個(gè)微任務(wù)(關(guān)于Node與瀏覽器的 Event Loop 差異酬蹋,下文還會(huì)詳細(xì)介紹)及老。

3.Micro-Task 與 Macro-Task

Node端事件循環(huán)中的異步隊(duì)列也是這兩種:macro(宏任務(wù))隊(duì)列和 micro(微任務(wù))隊(duì)列。

  • 常見的 macro-task 比如:setTimeout范抓、setInterval骄恶、 setImmediate、script(整體代碼)匕垫、 I/O 操作等僧鲁。
  • 常見的 micro-task 比如: process.nextTick、new Promise().then(回調(diào))等。

4.注意點(diǎn)

(1) setTimeout 和 setImmediate

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

  • setImmediate 設(shè)計(jì)在poll階段完成時(shí)執(zhí)行,即check階段春寿;
  • setTimeout 設(shè)計(jì)在poll階段為空閑時(shí)朗涩,且設(shè)定時(shí)間到達(dá)后執(zhí)行,但它在timer階段執(zhí)行
setTimeout(function timeout () {
  console.log('timeout');
},0);
setImmediate(function immediate () {
  console.log('immediate');
});

  • 對(duì)于以上代碼來說绑改,setTimeout 可能執(zhí)行在前谢床,也可能執(zhí)行在后。
  • 首先 setTimeout(fn, 0) === setTimeout(fn, 1)绢淀,這是由源碼決定的
    進(jìn)入事件循環(huán)也是需要成本的萤悴,如果在準(zhǔn)備時(shí)候花費(fèi)了大于 1ms 的時(shí)間瘾腰,那么在 timer 階段就會(huì)直接執(zhí)行 setTimeout 回調(diào)
  • 如果準(zhǔn)備時(shí)間花費(fèi)小于 1ms皆的,那么就是 setImmediate 回調(diào)先執(zhí)行了

但當(dāng)二者在異步i/o callback內(nèi)部調(diào)用時(shí),總是先執(zhí)行setImmediate蹋盆,再執(zhí)行setTimeout

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

在上述代碼中费薄,setImmediate 永遠(yuǎn)先執(zhí)行。因?yàn)閮蓚€(gè)代碼寫在 IO 回調(diào)中栖雾,IO 回調(diào)是在 poll 階段執(zhí)行楞抡,當(dāng)回調(diào)執(zhí)行完畢后隊(duì)列為空,發(fā)現(xiàn)存在 setImmediate 回調(diào)析藕,所以就直接跳轉(zhuǎn)到 check 階段去執(zhí)行回調(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í)行筹煮。

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

五、Node與瀏覽器的 Event Loop 差異

瀏覽器環(huán)境下居夹,microtask的任務(wù)隊(duì)列是每個(gè)macrotask執(zhí)行完之后執(zhí)行败潦。而在Node.js中,microtask會(huì)在事件循環(huán)的各個(gè)階段之間執(zhí)行准脂,也就是一個(gè)階段執(zhí)行完畢劫扒,就會(huì)去執(zhí)行microtask隊(duì)列的任務(wù)

接下我們通過一個(gè)例子來說明兩者區(qū)別:

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é)果:timer1=>promise1=>timer2=>promise2

瀏覽器端的處理過程如下:

Node端運(yùn)行結(jié)果分兩種情況:

  • 如果是node11版本一旦執(zhí)行一個(gè)階段里的一個(gè)宏任務(wù)(setTimeout,setInterval和setImmediate)就立刻執(zhí)行微任務(wù)隊(duì)列狸膏,這就跟瀏覽器端運(yùn)行一致沟饥,最后的結(jié)果為timer1=>promise1=>timer2=>promise2
  • 如果是node10及其之前版本:要看第一個(gè)定時(shí)器執(zhí)行完,第二個(gè)定時(shí)器是否在完成隊(duì)列中。
    • 如果是第二個(gè)定時(shí)器還未在完成隊(duì)列中闷板,最后的結(jié)果為timer1=>promise1=>timer2=>promise2
    • 如果是第二個(gè)定時(shí)器已經(jīng)在完成隊(duì)列中澎灸,則最后的結(jié)果為timer1=>timer2=>promise1=>promise2(下文過程解釋基于這種情況下)

1.全局腳本(main())執(zhí)行,將2個(gè)timer依次放入timer隊(duì)列遮晚,main()執(zhí)行完畢性昭,調(diào)用棧空閑县遣,任務(wù)隊(duì)列開始執(zhí)行糜颠;

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

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

Node端的處理過程如下:


六穆端、總結(jié)

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

  • Node端仿便,microtask 在事件循環(huán)的各個(gè)階段之間執(zhí)行
  • 瀏覽器端体啰,microtask 在事件循環(huán)的 macrotask 執(zhí)行完之后執(zhí)行

作者:浪里行舟
鏈接:瀏覽器與Node的事件循環(huán)(Event Loop)有何區(qū)別?
來源:github
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)嗽仪,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處荒勇。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市钦幔,隨后出現(xiàn)的幾起案子枕屉,更是在濱河造成了極大的恐慌,老刑警劉巖鲤氢,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搀擂,死亡現(xiàn)場離奇詭異,居然都是意外死亡卷玉,警方通過查閱死者的電腦和手機(jī)哨颂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來相种,“玉大人威恼,你說我怎么就攤上這事。” “怎么了箫措?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵腹备,是天一觀的道長。 經(jīng)常有香客問我斤蔓,道長植酥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任弦牡,我火速辦了婚禮友驮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘驾锰。我一直安慰自己卸留,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布椭豫。 她就那樣靜靜地躺著耻瑟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捻悯。 梳的紋絲不亂的頭發(fā)上匆赃,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天淤毛,我揣著相機(jī)與錄音今缚,去河邊找鬼。 笑死低淡,一個(gè)胖子當(dāng)著我的面吹牛姓言,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蔗蹋,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼何荚,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了猪杭?” 一聲冷哼從身側(cè)響起餐塘,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎皂吮,沒想到半個(gè)月后戒傻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜂筹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年需纳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艺挪。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡不翩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情口蝠,我是刑警寧澤器钟,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站妙蔗,受9級(jí)特大地震影響俱箱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灭必,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一狞谱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧禁漓,春花似錦跟衅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至秘狞,卻和暖如春叭莫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背烁试。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工雇初, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人减响。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓靖诗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親支示。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刊橘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容