当Event Loop遇到异步的代码时,会被挂起并在需要执行的时候加入到 Task(有多种 Task) 队列中。
一旦执行栈为空,Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步还是同步行为。
不同的任务源会被分配到不同的 Task 队列中,任务源可以分为微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobs,macrotask 称为 task。可以把 await 看成是让出线程的标志。
微任务包括 process.nextTick ,promise ,MutationObserver。
宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI
Event Loop 执行顺序如下所示:
- 首先执行同步代码,这属于宏任务;
- 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行;
执行所有微任务; - 当执行完所有微任务后,如有必要会渲染页面;
- 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数。
代码示例:
console.log('script start');//1.同步代码,宏任务
async function async1() {
await async2();//跳出,执行async2()
console.log('async1 end');//5.异步代码,微任务
}
async function async2() {
console.log('async2 end');//2.
}
async1();//同步代码,宏任务
setTimeout(function() {
console.log('setTimeout');//8.异步代码,宏任务
}, 0);
new Promise(resolve => {//同步代码,微任务
console.log('Promise');//3.
resolve()
})
.then(function() {
console.log('promise1');//6.异步代码,微任务
})
.then(function() {
console.log('promise2');//7.异步代码,微任务
});
console.log('script end');//4.//同步代码,宏任务
- 首先先来解释下上述代码的 async 和 await 的执行顺序。
- 当我们调用 async1 函数时,会马上输出 async2 end,并且函数返回一个 Promise。
- 接下来在遇到 await的时候会就让出线程开始执行 async1 外的代码,所以我们完全可以把 await 看成是让出线程的标志。
- 所以以上代码虽然 setTimeout 写在 Promise 之前,但是因为 Promise 属于微任务而 setTimeout 属于宏任务,所以会有以上的打印。
根据个人理解及部分资料得出以上结论,如有错误,请批评指正。
本文探讨了浏览器中的Event Loop机制及其对异步代码执行的影响。当执行栈为空,Event Loop会从Task队列取出任务执行。任务源分为微任务和宏任务,例如process.nextTick、Promise和MutationObserver属于微任务,而setTimeout、setInterval等属于宏任务。异步代码执行顺序遵循先同步后异步,先微任务后宏任务的原则。async和await用于控制异步流程,await会让出线程执行其他代码,保证了Promise等微任务的优先执行。
647

被折叠的 条评论
为什么被折叠?



