共计 1757 个字符,预计需要花费 5 分钟才能阅读完成。
任务队列
首先我们要知道关于 JavaScript 的一些规则:
JavaScript 是被设计成单线程的
JavaScript 的任务分为同步任务和异步任务
同步任务都在主线程上执行,形成一个执行栈。当主线程执行完之后,运行微任务(micro-task)队列的任务直到为空,更新 UI 渲染(会根据浏览器的逻辑,决定要不要马上执行更新),然后再运行宏任务(macro-task)队列的任务直到为空 …… 流程如下:
(主线程上的执行栈同步任务,可以视为是第一个 macro-task 队列)
macro-task -> micro-task(如果存在) -> 更新 UI 渲染
如此无限循环上面的流程,是为 JavaScript 的 Event Loop 机制。
宏任务
宏任务(macro-task),宏任务队列可以有一个或者多个。每个任务都有一个任务源 (task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。
宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering.
微任务
微任务(micro-task),微任务在渲染更新前,macro-task 之后执行。关于 async 和 await,因为 async await 本身就是 promise+generator 的语法糖。所以 await 后面的代码是 microtask。实际上 await 是一个让出线程的标志。await 后面的表达式会先执行一遍,将 await 后面的代码加入到 microtask 中,然后就会跳出整个 async 函数来执行后面的代码。
微任务:process.nextTick, Promise, Object.observer, MutationObserver,await.
举例
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’);
}, 0)
async1();
new Promise(function (resolve) {
console.log(‘promise1’);
resolve();
}).then(function () {
console.log(‘promise2’);
});
console.log(‘script end’);
流程如下:
console 打印 script start
setTimeout,是异步宏任务,进入 macro-task setTimeout 队列
async1(), async await 函数,在 await 之前是同步任务,直接执行,打印 async1 start
await async2(),await 后面的表达式会先执行一遍,打印 async2
await 下面的代码视为 promise.then,进入 micro-task promise 队列,跳出 async1()
new Promise,promise 内,.then 之前的代码是直接执行的,所以打印 promise1
.then 内函数进入 micro-task promise 队列后
console,直接打印 script end
主线程执行栈运行完并清空了,micro-task 进入执行栈,分别按顺序执行打印 async1 end 和 promise2。
micro-task 队列清空,macro-task 进入执行栈,打印 setTimeout,程序运行完毕。
完整结果如下:
/**
*script start
*async1 start
*async2
*promise1
*script end
*async1 end
*promise2
*setTimeout
*/
该结果基于 chrome 版本 72.0.3626.121。因为 async await 标准有所改变,所以稍老版本的浏览器结果可能不一致。
参考:https://jakearchibald.com/201…https://github.com/Advanced-F…