JS是一个单线程,JS任务分为同步任务和异步任务。
同步任务在主线程中按顺序执行,异步任务不进入主线程,而是进入任务队列中,只有异步任务通知主线程可以执行了,该任务才会进入主线程执行。
异步任务又分为宏任务、微任务
常见宏任务
- script
- setTimeout、setInterval
- UI rendering/UI事件
- postMessage、MessageChannel
- setImmediate、I/O(Node.js)
setImmediate方法则是在当前"任务队列"的尾部添加事件
常见微任务
- Promise
- process.nextTick(Node.js)
process.nextTick方法可以在当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")之前----触发回调函数
事件执行顺序
- 页面执行JS脚本,所有同步任务形成一个执行栈
- 主线程外还有一个任务队列,异步任务有了运行结果,就会在任务队列中放入一个事件
- 执行栈中所有任务完成后,就会去任务队列中读取是否有可执行任务
- 先执行完队列中所有微任务
- 执行下一个宏任务
- 在执行完微任务
- 重复5~6
案例
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('settimeout')
})
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
输出
script start、async1 start、async2、promise1、script end、async1 end、promise2、settimeout
分析
- 执行整段代码,遇到 console.log(‘script start’) 直接打印结果,输出 script start
- 遇到定时器了,它是宏任务,先放着不执行
- 遇到 async1(),执行 async1 函数,先打印 async1 start,下面遇到await怎么办?先执行 async2,打印 async2,然后阻塞下面代码(即加入微任务列表),跳出去执行同步代码
- 跳到 new Promise 这里,直接执行,打印 promise1,下面遇到 .then(),它是微任务,放到微任务列表等待执行
- 最后一行直接打印 script end,现在同步代码执行完了,开始执行微任务,即 await 下面的代码,打印 async1 end
- 继续执行下一个微任务,即执行 then 的回调,打印 promise2
- 上一个宏任务所有事都做完了,开始下一个宏任务,就是定时器,打印 settimeout