事件循环机制
js中的代码,其上下文进入执行栈之后,引擎会判断是不是异步操作(如DOM事件、timer、异步请求)。如果是异步任务,浏览器会将其上下文移出执行栈,交给浏览器的其他模块(如chrome的webcore模块)去处理。等到其达到触发条件后,再将其对应的回调函数添加到 任务队列 (task queue)中去。等执行栈中的任务清空后,引擎会将任务队列中的回调函数重新压入执行栈。
微任务与宏任务
执行栈清空完毕后,引擎会将任务队列中的任务(回调函数)重新压入执行栈。这些任务分为宏任务(macrotasks)和微任务(microtasks)
- 宏任务(macrotasks): script(整体代码),setTimeout, setInterval, setImmediate, I/O, UI rendering 等对应的回调
- 微任务(microtasks): process.nextTick, Promises, Object.observe(废弃), MutationObserver 等对应的回调
在Promise/A+的规范中,Promise
的实现可以是微任务,也可以是宏任务。但是大多数原生实现的Promise都是微任务。比如chrome、node。但一些polyfill中实现的Promise,其实就是宏任务。
每次事件循环,是先执行一个宏任务、然后执行所有的微任务。因为script(整体代码)也是一个宏任务,所以执行栈清空后,先取的任务是所有微任务。这是符合先宏任务、然后所有微任务的。
例:
let p = new Promise(function (resolve) {
console.log(1)
resolve()
})
p.then(function () {
console.log(2)
}).then(function () {
console.log(3)
Promise.resolve().then(function() {
console.log(4)
})
})
setTimeout(function () {
console.log(5)
},0)
其过程为:
log(1)入栈,执行完毕
第一个then、第二个then入栈,其回调被添加到微任务队列
settimeout入栈,其回调被添加到宏任务队列
执行栈清空,第一个微任务入栈并执行完毕(整体代码也相当于一个宏任务,所以开始所有微任务)
第二个微任务入栈,又插入了一个新的微任务log(4) ,第二个执行完毕
微任务log(4)入栈并执行完毕,此时所有微任务被清空
开始下一轮循环
宏任务log(5)入栈...
网友评论