一次搞懂- JS 事件循環(huán)之宏任務(wù)和微任務(wù)
作者:九旬
來源:SegmentFault 思否社區(qū)
眾所周知,JS 是一門單線程語言,可是瀏覽器又能很好的處理異步請(qǐng)求,那么到底是為什么呢?
JS 的執(zhí)行環(huán)境一般是瀏覽器和 Node.js,兩者稍有不同,這里只討論瀏覽器環(huán)境下的情況。
JS 執(zhí)行過程中會(huì)產(chǎn)生兩種任務(wù),分別是:同步任務(wù)和異步任務(wù)。
同步任務(wù):比如聲明語句、for、賦值等,讀取后依據(jù)從上到下從左到右,立即執(zhí)行。 異步任務(wù):比如 ajax 網(wǎng)絡(luò)請(qǐng)求,setTimeout 定時(shí)函數(shù)等都屬于異步任務(wù)。異步任務(wù)會(huì)通過任務(wù)隊(duì)列(Event Queue)的機(jī)制(先進(jìn)先出的機(jī)制)來進(jìn)行協(xié)調(diào)。
任務(wù)隊(duì)列(Event Queue)
宏任務(wù)主要包括:scrip(JS 整體代碼)、setTimeout、setInterval、setImmediate、I/O、UI 交互 微任務(wù)主要包括:Promise(重點(diǎn)關(guān)注)、process.nextTick(Node.js)、MutaionObserver

理解微任務(wù)和宏任務(wù)的執(zhí)行執(zhí)行過程
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0);
Promise.resolve()
.then(function () {
console.log("promise1");
})
.then(function () {
console.log("promise2");
});
console.log("script end");
宏任務(wù):執(zhí)行整體代碼(相當(dāng)于 <script>中的代碼):輸出: script start遇到 setTimeout,加入宏任務(wù)隊(duì)列,當(dāng)前宏任務(wù)隊(duì)列(setTimeout) 遇到 promise,加入微任務(wù),當(dāng)前微任務(wù)隊(duì)列(promise1) 輸出: script end微任務(wù):執(zhí)行微任務(wù)隊(duì)列(promise1) 輸出: promise1,then 之后產(chǎn)生一個(gè)微任務(wù),加入微任務(wù)隊(duì)列,當(dāng)前微任務(wù)隊(duì)列(promise2)執(zhí)行 then,輸出 promise2執(zhí)行渲染操作,更新界面(敲黑板劃重點(diǎn))。 宏任務(wù):執(zhí)行 setTimeout 輸出: setTimeout
Promise 的執(zhí)行
new Promise(..)中的代碼,也是同步代碼,會(huì)立即執(zhí)行。只有then之后的代碼,才是異步執(zhí)行的代碼,是一個(gè)微任務(wù)。console.log("script start");
setTimeout(function () {
console.log("timeout1");
}, 10);
new Promise((resolve) => {
console.log("promise1");
resolve();
setTimeout(() => console.log("timeout2"), 10);
}).then(function () {
console.log("then1");
});
console.log("script end");
當(dāng)前任務(wù)隊(duì)列:微任務(wù): [], 宏任務(wù):[ <script>]
宏任務(wù): 輸出: script start遇到 timeout1,加入宏任務(wù) 遇到 Promise,輸出 promise1,直接 resolve,將 then 加入微任務(wù),遇到 timeout2,加入宏任務(wù)。輸出 script end宏任務(wù)第一個(gè)執(zhí)行結(jié)束
當(dāng)前任務(wù)隊(duì)列:微任務(wù)[then1],宏任務(wù)[timeou1, timeout2]
微任務(wù): 執(zhí)行 then1,輸出 then1微任務(wù)隊(duì)列清空
當(dāng)前任務(wù)隊(duì)列:微任務(wù)[],宏任務(wù)[timeou1, timeout2]
宏任務(wù): 輸出 timeout1輸出 timeout2
當(dāng)前任務(wù)隊(duì)列:微任務(wù)[],宏任務(wù)[timeou2]
微任務(wù): 為空跳過
當(dāng)前任務(wù)隊(duì)列:微任務(wù)[],宏任務(wù)[timeou2]
宏任務(wù): 輸出 timeout2
async/await 的執(zhí)行
// async/await 寫法
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
// Promise 寫法
async function async1() {
console.log("async1 start");
Promise.resolve(async2()).then(() => console.log("async1 end"));
}
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
setTimeout(() => {
console.log("timeout");
}, 0);
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});
console.log("script end");
當(dāng)前任務(wù)隊(duì)列:宏任務(wù):[ <script>],微任務(wù): []
宏任務(wù): 輸出: async1 start遇到 async2,輸出: async2,并將 then(async1 end)加入微任務(wù)遇到 setTimeout,加入宏任務(wù)。 遇到 Promise,輸出: promise1,直接 resolve,將 then(promise2)加入微任務(wù)輸出: script end
當(dāng)前任務(wù)隊(duì)列:微任務(wù)[promise2, async1 end],宏任務(wù)[timeout]
微任務(wù): 輸出: promise2promise2 出隊(duì) 輸出: async1 endasync1 end 出隊(duì) 微任務(wù)隊(duì)列清空
當(dāng)前任務(wù)隊(duì)列:微任務(wù)[],宏任務(wù)[timeout]
宏任務(wù): 輸出: timeouttimeout 出隊(duì),宏任務(wù)清空
setTimerout 并不準(zhǔn)確
const s = new Date().getSeconds();
console.log("script start");
new Promise((resolve) => {
console.log("promise");
resolve();
}).then(() => {
console.log("then1");
while (true) {
if (new Date().getSeconds() - s >= 4) {
console.log("while");
break;
}
}
});
setTimeout(() => {
console.log("timeout");
}, 2000);
console.log("script end");
四秒后輸出:while、timeout
<!-- 以 Vue 為例 nextTick -->
總結(jié)

評(píng)論
圖片
表情
