持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
在学习事件循环之前,需要了解一些JS异步的知识,前面有介绍:juejin.cn/post/710355…
因为js是单线程的,所以为了解决单线程运行阻塞的问题,使用异步的方式编程。 而js的异步任务又分为:宏任务和微任务,本篇我们来详细了解一下。
1、分类
- JS任务
- 同步任务
- 异步任务
- 宏任务(macrotask)
- 整体代码
- setTimeOut
- setInterval
- Ajax
- I/O操作
- 微任务(microtask)
- Promise.then、Promise.catch、Promise.finally
- process.nextTick
- MutationObserve(前端回溯)
- 宏任务(macrotask)
2、为什么引入微任务?
队列具有先进先出的性质,当有时需要处理高优先级的任务,就需要执行微任务。
当一个宏任务执行完毕,会进入任务队列将里面的微任务执行完,再进入下一个宏任务。
3、执行顺序:
- 在执行的时候,先执行同步任务(主线程执行栈中的代码)
- 遇到异步任务,放入相应的任务队列中,宏任务放入宏任务队列,微任务放入微任务队列
- 同步代码执行完毕后,将异步的微任务从队列中的代码调入主线程执行
- 微任务执行完毕后,将异步宏任务从队列中的代码调入主线程执行
- EventLoop(事件循环)直到结束
关于Node中的执行顺序:version 10 之后和浏览器的行为相同
4、面试常见题及分析
题目:
async function F1(){
console.log('F1 Start');
await F2();
console.log('F1 End');
}
async function F2(){
console.log('F2');
}
console.log('Script Start');
setTimeout(function() {
console.log('setTimeout');
},1000)
F1();
new Promise(function(resolve) {
console.log('Promise1');
resolve()
}).then(function() {
console.log('Promise2');
})
console.log('Script End');
解析:
-
输出'Script Start'
-
setTimeout是宏任务,当前执行的是整体代码,属于宏任务,所以
setTimeout
放入宏任务队列下次执行 -
执行F1()
1. 输出'F1 Start' 2. 执行await F2(),这里相当于放入new Promise,后面的所有语句放入.then()执行。 所以先执行F2(),输出'F2',.then()中的内容属于微任务,放入微任务队列。 new Promise(function(resolve) { F2(); }).then(function() { console.log('F1 End'); })
-
执行Promise,输出'Promise1',.then()中的内容放入微任务队列。
-
输出'Script End'
-
此时宏任务队列中有setTimeout,微任务队列有 输出'F1 End'语句、输出'Promise2'语句
-
先执行微任务,先进先出的顺序。输出'F1 End' 、'Promise2'
-
执行宏任务setTimeout,输出'setTimeout' 最终的输出顺序是:
- Script Start
- F1 Start
- F2
- Promise1
- Script End
- F1 End
- Promise2
- setTimeout