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);