enent Loop 即時事件循環,在浏覽器或者node環境下js單線程運作時不會阻塞的一種機制,也就是我們所了解的異步原理
js的運作機制是分為堆、棧和隊列,如圖所示:
而在js機制中,js是分為宏任務(MacroTask)也叫Task和微任務(MicroTask)的,浏覽器在運作的時候會首先清空微任務,然後才會執行宏任務。
mask宏任務:
script全部代碼、setTimeout、setInterval、setImmediate(浏覽器暫時不支援,隻有IE10支援,具體可見MDN)、I/O、UI Rendering
MicroTask微任務:
Process.nextTick(Node獨有,vue裡面有用)、Promise、Object.observe(廢棄)、MutationObserver,詳情可檢視這裡
舉例如下
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');
注意:setTimeout是宏任務,Promise是微任務(如果是new Promise則是宏任務)
第一次執行:
執行同步代碼,将宏任務(Tasks)和微任務(Microtasks)劃分到各自隊列中。
log:script start、script end
第二次執行:
執行宏任務後,檢測到微任務(Microtasks)隊列中不為空,執行Promise1,執行完成Promise1後,調用Promise2.then,放入微任務(Microtasks)隊列中,再執行Promise2.then。
log:script start、script end、promise1、promise2
第三次執行:
當微任務(Microtasks)隊列中為空時,執行宏任務(Tasks),執行setTimeout callback,列印日志。
log:script start、script end、promise1、promise2、setTimeout
console.log('script start') //1
async function async1() {
await async2()
console.log('async1 end') //5
}
async function async2() {
console.log('async2 end') //2
}
async1()
setTimeout(function () {
console.log('setTimeout') //8
}, 0)
new Promise(resolve => {
console.log('Promise') //3
resolve()
})
.then(function () {
console.log('promise1') //6
})
.then(function () {
console.log('promise2') //7
})
console.log('script end')//4
首先,列印script start,調用async1()時,傳回一個Promise,是以列印出來async2 end。
每個 await,會新産生一個promise,但這個過程本身是異步的,是以該await後面不會立即調用。
繼續執行同步代碼,列印Promise和script end,将then函數放入微任務隊列中等待執行。
同步執行完成之後,檢查微任務隊列是否為null,然後按照先入先出規則,依次執行。
然後先執行列印promise1,此時then的回調函數傳回undefinde,此時又有then的鍊式調用,又放入微任務隊列中,再次列印promise2。
再回到await的位置執行傳回的 Promise 的 resolve 函數,這又會把 resolve 丢到微任務隊列中,列印async1 end。
當微任務隊列為空時,執行宏任務,列印setTimeout。
Process.nextTick()(vue中就是使用這個來進行插隊的)
let bar;
setTimeout(() => {
console.log('setTimeout');
}, 0)
setImmediate(() => {
console.log('setImmediate');
})
function someAsyncApiCall(callback) {
process.nextTick(callback);
}
someAsyncApiCall(() => {
console.log('bar', bar); // 1
});
bar = 1;
結果:
bar 1
setTimeout
setImmediate
首先執行
process.nextTick(callback);