在 js 的运行机制中,主要把任务分为同步任务和异步任务,其中异步任务又分为宏任务和微任务。
js 运行时分为主线程和调用栈(执行栈),所有的同步任务都会放到调用栈中按照顺序等待主线程依次执行,而异步任务会在异步任务得到结果后,将注册的回到函数放到异步任务队列中等待主线程空闲的时候(执行栈被清空)通知调用,将先进入的异步任务放置执行栈中执行。
在执行完成之后,在任务队列中删除任务。此时的任务称为宏任务 。
当执行栈把某个宏任务执行完成之后,会去通知微任务队列执行,直到清空微任务队列中的微任务再进行下一次循环。
宏任务:所有 js 代码,setTimeout,setInterval,setImmediate,I/O,UI Rendering
微任务:promise 的 then,catch,finaly,process.nextTick,MutationObserver(监测 dom 的变更)
延伸: 有关 async 和 await 的执行顺序:async/await 在底层转换成了 promise 和 then 回调函数。
也就是说,这是 promise 的语法糖。每次我们使用 await, 解释器都创建一个 promise 对象,然后把剩下的 async 函数中的操作放到 then 回调函数中。
例如下面的例子的执行顺序:
console.log('script start')
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise') resolve()
}).then(function() {
console.log('promise1')
}) .then(function() {
console.log('promise2')
})
console.log('script end')
执行顺序为:
script start
async1 start
async2 end
Promise
script end
async1 end
promise1
promise2
setTimeout
这里的 async1 end 作为微任务处理了
为什么settime out 最后一个执行,这应该是宏任务啊
单纯评价宏任务和微任务的先后顺序可能是有问题的,在宏任务中会产生微任务,然而产生宏任务的主线也会产生微任务,在调用栈闲置的时候会先清空微任务队列,然后再去执行宏任务队列。当然,在宏任务队列执行的时候又会产生新的微任务队列。
所以,没有单纯的谁先执行,要看场景。