12.事件循环-微任务与宏任务

事件循环机制
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)入栈...

每次宏任务完成后,都会稍作停顿,更新UI,然后再进行下一个宏任务,以避免卡顿。所以操作dom之类的操作,放在微任务里更合适。【参考 知乎--Vue 中如何使用 MutationObserver 做批量处理?】

async 函数在实现上可以看作是promise的语法糖,

async function f() {
        console.log(1)
}
// 等价于
new Promise(function(resolve, reject) {
    console.log(1)
    resolve();
})

所以下面这个例子:

console.log(1)
f1()

async function f1() {
        console.log(2)
        await f2()
        console.log(3)
}
async function f2() {
        console.log(4)
}

console.log(5)

// 1 2 4 5 3

相当于:

console.log(1)

new Promise(function(resolve1) {
    console.log(2)

    new Promise(function(resolve2) {
        console.log(4)
        resolve2()
    }).then(res => {
        console.log(3)
    })
})

console.log(5)

你可能感兴趣的:(12.事件循环-微任务与宏任务)