偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

熟悉事件循環(huán)?為什么會(huì)分為宏任務(wù)和微任務(wù)

開發(fā) 前端
事件循環(huán)中的任務(wù)被分為宏任務(wù)和微任務(wù),是為了給高優(yōu)先級(jí)任務(wù)一個(gè)插隊(duì)的機(jī)會(huì):微任務(wù)比宏任務(wù)有更高優(yōu)先級(jí)。

什么是事件循環(huán)

在了解事件循環(huán)前,需要一些有關(guān) JS 特性的前置知識(shí)。

JS 引擎是單線程的,直白來說就是一個(gè)時(shí)間點(diǎn)下 JS 引擎只能去做一件事情,而 Java 這種多線程語言,可以同時(shí)做幾件事情。

JS 做的任務(wù)分為同步和異步兩種,所謂 "異步",簡(jiǎn)單說就是一個(gè)任務(wù)不是連續(xù)完成的,先執(zhí)行第一段,等做好了準(zhǔn)備,再回過頭執(zhí)行第二段,第二段也被叫做回調(diào);同步則是連貫完成的。

像讀取文件、網(wǎng)絡(luò)請(qǐng)求這種任務(wù)屬于異步任務(wù):花費(fèi)時(shí)間很長,但中間的操作不需要 JS 引擎自己完成,它只用等別人準(zhǔn)備好了,把數(shù)據(jù)給他,他再繼續(xù)執(zhí)行回調(diào)部分。

如果沒有特殊處理,JS 引擎在執(zhí)行異步任務(wù)時(shí),應(yīng)該是存在等待的,不去做任何其他事情。用一個(gè)圖來展示這個(gè)過程,可以看出,在執(zhí)行異步任務(wù)時(shí)有大量的空閑時(shí)間被浪費(fèi)。

實(shí)際上這是大多數(shù)多線程語言的處理辦法。但對(duì)于 JS 這種單線程語言來說,這種長時(shí)間的空閑等待是不可接受的:遇到其他緊急任務(wù),Java 可以再開一個(gè)線程去處理,JS 卻只能忙等。

所以采取了以下的“異步任務(wù)回調(diào)通知”模式:

在等待異步任務(wù)準(zhǔn)備的同時(shí),JS 引擎去執(zhí)行其他同步任務(wù),等到異步任務(wù)準(zhǔn)備好了,再去執(zhí)行回調(diào)。這種模式的優(yōu)勢(shì)顯而易見,完成相同的任務(wù),花費(fèi)的時(shí)間大大減少,這種方式也被叫做非阻塞式。

而實(shí)現(xiàn)這個(gè)“通知”的,正是事件循環(huán),把異步任務(wù)的回調(diào)部分交給事件循環(huán),等時(shí)機(jī)合適交還給 JS 線程執(zhí)行。事件循環(huán)并不是 JavaScript 首創(chuàng)的,它是計(jì)算機(jī)的一種運(yùn)行機(jī)制。

事件循環(huán)是由一個(gè)隊(duì)列組成的,異步任務(wù)的回調(diào)遵循先進(jìn)先出,在 JS 引擎空閑時(shí)會(huì)一輪一輪地被取出,所以被叫做循環(huán)。

根據(jù)隊(duì)列中任務(wù)的不同,分為宏任務(wù)和微任務(wù)。

宏任務(wù)和微任務(wù)

事件循環(huán)由宏任務(wù)和在執(zhí)行宏任務(wù)期間產(chǎn)生的所有微任務(wù)組成。完成當(dāng)下的宏任務(wù)后,會(huì)立刻執(zhí)行所有在此期間入隊(duì)的微任務(wù)。

這種設(shè)計(jì)是為了給緊急任務(wù)一個(gè)插隊(duì)的機(jī)會(huì),否則新入隊(duì)的任務(wù)永遠(yuǎn)被放在隊(duì)尾。區(qū)分了微任務(wù)和宏任務(wù)后,本輪循環(huán)中的微任務(wù)實(shí)際上就是在插隊(duì),這樣微任務(wù)中所做的狀態(tài)修改,在下一輪事件循環(huán)中也能得到同步。

常見的宏任務(wù)有:script(整體代碼)/setTimout/setInterval/setImmediate(node 獨(dú)有)/requestAnimationFrame(瀏覽器獨(dú)有)/IO/UI render(瀏覽器獨(dú)有)

常見的微任務(wù)有:process.nextTick(node 獨(dú)有)/Promise.then()/Object.observe/MutationObserver

宏任務(wù) setTimeout 的誤區(qū)

setTimeout 的回調(diào)不一定在指定時(shí)間后能執(zhí)行。而是在指定時(shí)間后,將回調(diào)函數(shù)放入事件循環(huán)的隊(duì)列中。

如果時(shí)間到了,JS 引擎還在執(zhí)行同步任務(wù),這個(gè)回調(diào)函數(shù)需要等待;如果當(dāng)前事件循環(huán)的隊(duì)列里還有其他回調(diào),需要等其他回調(diào)執(zhí)行完。

另外,setTimeout 0ms 也不是立刻執(zhí)行,它有一個(gè)默認(rèn)最小時(shí)間,為 4ms。所以下面這段代碼的輸出結(jié)果不一定:

// node
setTimeout(() => {
console.log('setTimeout')
}, 0)
setImmediate(() => {
console.log('setImmediate')
})

因?yàn)槿〕龅谝粋€(gè)宏任務(wù)之前在執(zhí)行全局 Script,如果這個(gè)時(shí)間大于 4ms,這時(shí) setTimeout 的回調(diào)函數(shù)已經(jīng)放入隊(duì)列,就先執(zhí)行 setTimeout;如果準(zhǔn)備時(shí)間小于 4ms,就會(huì)先執(zhí)行 setImmediate。

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

瀏覽器的事件循環(huán)由一個(gè)宏任務(wù)隊(duì)列+多個(gè)微任務(wù)隊(duì)列組成。

首先,執(zhí)行第一個(gè)宏任務(wù):全局 Script 腳本。產(chǎn)生的的宏任務(wù)和微任務(wù)進(jìn)入各自的隊(duì)列中。執(zhí)行完 Script 后,把當(dāng)前的微任務(wù)隊(duì)列清空。完成一次事件循環(huán)。

接著再取出一個(gè)宏任務(wù),同樣把在此期間產(chǎn)生的回調(diào)入隊(duì)。再把當(dāng)前的微任務(wù)隊(duì)列清空。以此往復(fù)。

宏任務(wù)隊(duì)列只有一個(gè),而每一個(gè)宏任務(wù)都有一個(gè)自己的微任務(wù)隊(duì)列,每輪循環(huán)都是由一個(gè)宏任務(wù)+多個(gè)微任務(wù)組成。

下面的 Demo 展示了微任務(wù)的插隊(duì)過程:

Promise.resolve().then(()=>{
console.log('第一個(gè)回調(diào)函數(shù):微任務(wù)1')
setTimeout(()=>{
console.log('第三個(gè)回調(diào)函數(shù):宏任務(wù)2')
},0)
})
setTimeout(()=>{
console.log('第二個(gè)回調(diào)函數(shù):宏任務(wù)1')
Promise.resolve().then(()=>{
console.log('第四個(gè)回調(diào)函數(shù):微任務(wù)2')
})
},0)
// 第一個(gè)回調(diào)函數(shù):微任務(wù)1
// 第二個(gè)回調(diào)函數(shù):宏任務(wù)1
// 第四個(gè)回調(diào)函數(shù):微任務(wù)2
// 第三個(gè)回調(diào)函數(shù):宏任務(wù)2

打印的結(jié)果不是從 1 到 4,而是先執(zhí)行第四個(gè)回調(diào)函數(shù),再執(zhí)行第三個(gè),因?yàn)樗且粋€(gè)微任務(wù),比第三個(gè)回調(diào)函數(shù)有更高優(yōu)先級(jí)。

Node 的事件循環(huán)

node 的事件循環(huán)比瀏覽器復(fù)雜很多。由 6 個(gè)宏任務(wù)隊(duì)列+6 個(gè)微任務(wù)隊(duì)列組成。

宏任務(wù)按照優(yōu)先級(jí)從高到低依次是:

其執(zhí)行規(guī)律是:在一個(gè)宏任務(wù)隊(duì)列全部執(zhí)行完畢后,去清空一次微任務(wù)隊(duì)列,然后到下一個(gè)等級(jí)的宏任務(wù)隊(duì)列,以此往復(fù)。

一個(gè)宏任務(wù)隊(duì)列搭配一個(gè)微任務(wù)隊(duì)列。六個(gè)等級(jí)的宏任務(wù)全部執(zhí)行完成,才是一輪循環(huán)。

其中需要關(guān)注的是:Timers、Poll、Check 階段,因?yàn)槲覀兯鶎懙拇a大多屬于這三個(gè)階段。

  1. Timers:定時(shí)器 setTimeout/setInterval;
  2. Poll :獲取新的 I/O 事件, 例如操作讀取文件等;
  3. Check:setImmediate 回調(diào)函數(shù)在這里執(zhí)行;

除此之外,node 端微任務(wù)也有優(yōu)先級(jí)先后:

  1. process.nextTick;
  2. promise.then 等;

清空微任務(wù)隊(duì)列時(shí),會(huì)先執(zhí)行 process.nextTick,然后才是微任務(wù)隊(duì)列中的其他。下面這段代碼可以佐證瀏覽器和 node 的差異:

console.log('Script開始')
setTimeout(() => {
console.log('第一個(gè)回調(diào)函數(shù),宏任務(wù)1')
Promise.resolve().then(function() {
console.log('第四個(gè)回調(diào)函數(shù),微任務(wù)2')
})
}, 0)
setTimeout(() => {
console.log('第二個(gè)回調(diào)函數(shù),宏任務(wù)2')
Promise.resolve().then(function() {
console.log('第五個(gè)回調(diào)函數(shù),微任務(wù)3')
})
}, 0)
Promise.resolve().then(function() {
console.log('第三個(gè)回調(diào)函數(shù),微任務(wù)1')
})
console.log('Script結(jié)束')
node端:
Script開始
Script結(jié)束
第三個(gè)回調(diào)函數(shù),微任務(wù)1
第一個(gè)回調(diào)函數(shù),宏任務(wù)1
第二個(gè)回調(diào)函數(shù),宏任務(wù)2
第四個(gè)回調(diào)函數(shù),微任務(wù)2
第五個(gè)回調(diào)函數(shù),微任務(wù)3
瀏覽器
Script開始
Script結(jié)束
第三個(gè)回調(diào)函數(shù),微任務(wù)1
第一個(gè)回調(diào)函數(shù),宏任務(wù)1
第四個(gè)回調(diào)函數(shù),微任務(wù)2
第二個(gè)回調(diào)函數(shù),宏任務(wù)2
第五個(gè)回調(diào)函數(shù),微任務(wù)3

可以看出,在 node 端要等當(dāng)前等級(jí)的所有宏任務(wù)完成,才能輪到微任務(wù):第四個(gè)回調(diào)函數(shù),微任務(wù)2在兩個(gè) setTimeout 完成后才打印。

因?yàn)闉g覽器執(zhí)行時(shí)是一個(gè)宏任務(wù)+一個(gè)微任務(wù)隊(duì)列,而 node 是一整個(gè)宏任務(wù)隊(duì)列+一個(gè)微任務(wù)隊(duì)列。

node11.x 前后版本差異

node11.x 之前,其事件循環(huán)的規(guī)則就如上文所述:先取出完一整個(gè)宏任務(wù)隊(duì)列中全部任務(wù),然后執(zhí)行一個(gè)微任務(wù)隊(duì)列。

但在 11.x 之后,node 端的事件循環(huán)變得和瀏覽器類似:先執(zhí)行一個(gè)宏任務(wù),然后是一個(gè)微任務(wù)隊(duì)列。但依然保留了宏任務(wù)隊(duì)列和微任務(wù)隊(duì)列的優(yōu)先級(jí)。可以用下面的 Demo 佐證:

console.log('Script開始')
setTimeout(() => {
console.log('宏任務(wù)1(setTimeout)')
Promise.resolve().then(() => {
console.log('微任務(wù)promise2')
})
}, 0)
setImmediate(() => {
console.log('宏任務(wù)2')
})
setTimeout(() => {
console.log('宏任務(wù)3(setTimeout)')
}, 0)
console.log('Script結(jié)束')
Promise.resolve().then(() => {
console.log('微任務(wù)promise1')
})
process.nextTick(() => {
console.log('微任務(wù)nextTick')
})

在 node11.x 之前運(yùn)行:

Script開始
Script結(jié)束
微任務(wù)nextTick
微任務(wù)promise1
宏任務(wù)1(setTimeout)
宏任務(wù)3(setTimeout)
微任務(wù)promise2
宏任務(wù)2(setImmediate)

在 node11.x 之后運(yùn)行:

Script開始
Script結(jié)束
微任務(wù)nextTick
微任務(wù)promise1
宏任務(wù)1(setTimeout)
微任務(wù)promise2
宏任務(wù)3(setTimeout)
宏任務(wù)2(setImmediate)

可以發(fā)現(xiàn),在不同的 node 環(huán)境下:

  1. 微任務(wù)隊(duì)列中 process.nextTick 都有更高優(yōu)先級(jí),即使它后進(jìn)入微任務(wù)隊(duì)列,也會(huì)先打印微任務(wù)nextTick再微任務(wù)promise1;
  2. 宏任務(wù) setTimeout 比 setImmediate 優(yōu)先級(jí)更高,宏任務(wù)2(setImmediate)是三個(gè)宏任務(wù)中最后打印的;
  3. 在 node11.x 之前,微任務(wù)隊(duì)列要等當(dāng)前優(yōu)先級(jí)的所有宏任務(wù)先執(zhí)行完,在兩個(gè) setTimeout 之后才打印微任務(wù)promise2;在 node11.x 之后,微任務(wù)隊(duì)列只用等當(dāng)前這一個(gè)宏任務(wù)先執(zhí)行完。

結(jié)語

事件循環(huán)中的任務(wù)被分為宏任務(wù)和微任務(wù),是為了給高優(yōu)先級(jí)任務(wù)一個(gè)插隊(duì)的機(jī)會(huì):微任務(wù)比宏任務(wù)有更高優(yōu)先級(jí)。

node 端的事件循環(huán)比瀏覽器更復(fù)雜,它的宏任務(wù)分為六個(gè)優(yōu)先級(jí),微任務(wù)分為兩個(gè)優(yōu)先級(jí)。node 端的執(zhí)行規(guī)律是一個(gè)宏任務(wù)隊(duì)列搭配一個(gè)微任務(wù)隊(duì)列,而瀏覽器是一個(gè)單獨(dú)的宏任務(wù)搭配一個(gè)微任務(wù)隊(duì)列。但是在 node11 之后,node 和瀏覽器的規(guī)律趨同。

責(zé)任編輯:龐桂玉 來源: 前端大全
相關(guān)推薦

2020-12-29 08:21:03

JavaScript微任務(wù)宏任務(wù)

2021-07-24 11:15:19

開發(fā)技能代碼

2021-01-18 08:24:51

JavaScriptMicrotask微任務(wù)

2023-04-06 00:22:19

JavaScrip任務(wù)開發(fā)

2009-11-17 10:31:12

Solaris項(xiàng)目管理任務(wù)管理

2021-08-03 07:40:47

宏任務(wù)微任務(wù)React

2018-09-19 10:18:34

行式存儲(chǔ)列式存儲(chǔ)數(shù)據(jù)庫

2017-05-02 22:38:44

前端開發(fā)JS事件循環(huán)機(jī)制

2023-11-13 07:37:36

JS面試題線程

2019-05-14 13:42:16

物聯(lián)網(wǎng)安全物聯(lián)網(wǎng)IOT

2021-07-18 09:15:30

數(shù)據(jù)中心

2019-07-30 15:50:00

2022-06-20 07:44:34

ahooks定時(shí)器

2023-05-08 16:38:46

任務(wù)調(diào)度分布式任務(wù)調(diào)度

2025-02-24 09:00:00

CPUI/O密集型任務(wù)

2021-12-04 22:05:41

網(wǎng)頁任務(wù) Performanc

2019-11-14 10:00:18

Linuxcron任務(wù)自動(dòng)化任務(wù)

2023-08-11 16:28:24

2021-12-25 22:29:57

Node.js 微任務(wù)處理事件循環(huán)

2022-06-18 23:10:56

前端模塊循環(huán)依賴
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)