-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JS 基础篇 - 宏任务与微任务 & EventLoop事件循环 #49
Comments
2022.02.11 更新一道事件循环面试考题: async1 = async () => {
console.log('async1 start');
await async2();
console.log('async1 end');
}
function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
})
async1();
new Promise((resolve) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
return new Promise(function (resolve) {
resolve();
})
}).then((res) => {
console.log('promise3');
})
console.log('script end');
/**
* script start
* async1 start
* async2
* promise1
* script end
* async1 end
* promise2
* promise3
* setTimeout
*/ 解答
async1 = async () => {
// 4. 打印 'async1 start'
console.log('async1 start');
// 5. 执行 async2,遇到 await,将后续代码放入微任务队列等待执行,交还执行权(跳出该函数体)
await async2();
// 5.1 被放入微任务队列
// 10. 打印 async1 end
console.log('async1 end');
}
function async2() {
console.log('async2');
}
// 1. 打印 'script start'
console.log('script start');
// 2. 执行计时器,并向宏任务队列注册回调
setTimeout(() => {
console.log('setTimeout');
})
// 3. 执行 async1
async1();
// 6. 创建 promise 实例并执行 executor
new Promise((resolve) => {
// 7. 打印 promise1
console.log('promise1');
// 8. 执行 resolve() 并将 then 回调注册至微任务队列
resolve();
}).then(() => {
// 11. 打印 promise2
console.log('promise2');
return new Promise(function (resolve) {
// 12. 注册 then 回调至微任务队列
resolve();
})
}).then((res) => {
// 13. 打印 promise3
console.log('promise3');
})
// 9. 打印 script end
console.log('script end');
// 主线程清空,接着清空微任务队列,将微任务队列提到主线程执行👆 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
参考文章:
事件循环机制
概念
Event Loop 实际就是 JavaScript 异步执行机制的一种实现方式;
程序按照主线程-微任务-宏任务的顺序不断重复执行, 并始终维护各执行队列直至全部队列清空的操作就是 Event Loop;
流程
JavaScript 是单线程语言, JS 任务需要遵循一定的顺序执行。为了避免某个任务执行时间过长而阻塞后面任务的执行, JS 将任务分为了同步和异步任务,而同步任务和异步任务执行的场所不同,因此执行的过程也有所差异:
异步任务又被细分为宏任务和微任务, JS 在处理宏任务和微任务时又遵循特殊的执行顺序:
当 JS 遇到宏任务时, 将其放入 Macro Event Queue 中, 而微任务会被放入 Micro Event Queue 中(注意宏任务队列和微任务队列不是一个队列); 在读取(向外拿)回调函数时, 先清空微任务队列中的回调函数, 然后再从宏任务队列中调用并执行一个回调函数; (换句话说, 每一次宏任务执行前, 要清空上一次的微任务队列, 宏任务在微任务之后执行);
宏任务中可能产生新的微任务,而这些微任务的回调会被注册到微任务队列中,因此我们取出一个宏任务并执行完毕后,要再一次确认微任务队列是否被清空,若没有则要清空微任务队列,然后再从宏任务队列中调取下一个回调函数......
概括而言,JS 的执行顺序, 应遵循的思路为:
同步放入主线程 -> 执行同步代码(清空主线程代码) -> 遇到异步代码, 放入 Event Table -> Event Table 中判断宏任务 or 微任务 -> 注册回调放入各自队列 -> 若主线程为空,清空微任务(将微任务提到主线程执行) -> (若执行后又推入了新的微任务,又回到了主线程为空,微任务队列存在回调的情况,重复上述流程) -> ...... (若主线程/微任务队列都为空) -> 执行下一次宏任务(取出宏任务队列中的一个回调到主线程,然后执行。) -> … -> (若执行后推入了新的微任务,又回到了主线程为空,微任务队列存在回调的情况,重复上述流程)
宏任务 & 微任务
在通过例子深入了解 JS 执行机制前, 我们需要记住几个常用的宏任务和微任务:
宏任务:
微任务:
实例讲解
例一
例二
例三
经过上述三个例子, 你可能对 JS 执行机制有了大致的了解:
考核
回调函数内可能嵌套了多层, 但遵循上述步骤仍可以正确判断, 我们每次只需关注最外层嵌套函数即可, 在原有上下文基础上构建第二级的执行上下文, 清空主线程, 清空微队列, 再获取下一个宏任务回调执行并判断, 遇到嵌套后再循环…
The text was updated successfully, but these errors were encountered: