当EventLoop遇上Promise后,会有一些弯绕不过来,以下题目作为参考,题目参考自https://juejin.im/post/5c9a43175188252d876e5903,特整理一版,以便翻阅。
1. 得心应手版
考点:EventLoop中的执行顺序,宏任务 / 微任务的区别。
setTimeout(()=>{
console.log(1)
},0)
Promise.resolve().then(()=>{
console.log(2)
})
console.log(3)
// 输出 3 2 1
//如果不会这个 去网上重新学习吧
这里就有一个版本问题,是参考的笔者所指出的,这也是我看了一些文章后疑惑的点,被笔者讲清楚了,很赞,贴出来,防止以后忘记。
有时候会有版本是宏任务 ->微任务 -> 宏任务
,在这里笔者需要讲清楚一个概念,以免混淆。这里有个main script(主代码)的概念,就是一开始执行的代码(代码总要有开始执行的时候对吧,不然宏任务和微任务的队列哪里来的),这里被定义为了宏任务(笔者喜欢将main script的概念单独拎出来,不和两个任务队列混在一起),然后根据main script中产生的微任务队列和宏任务队列,分别清空,这个时候是先清空微任务的队列,再去清空宏任务的队列。
2. 游刃有余版
setTimeout(()=>{ // s1
console.log(1)
},0)
let a=new Promise((resolve)=>{
console.log(2)
resolve()
}).then(()=>{ // p1
console.log(3)
}).then(()=>{ // p2
console.log(4)
})
console.log(5)
// 输出 2 5 3 4 1
步骤:
1. 主代码执行
宏任务队列 = [s1]
console.log(2) ======================> 2
微任务队列 = [p1]
console.log(5) ======================> 5
2. 清空微任务
微任务队列 = []
console.log(3) ======================> 3
微任务队列 = [p2]
3. 清空微任务
微任务队列 = []
console.log(4) ======================> 4
4. 拿出第一个宏任务 s1
宏任务队列 = []
console.log(1) ======================> 1
注意:
这个要从Promise的实现来说,Promise的executor是一个同步函数,即非异步,立即执行的一个函数,因此他应该是和当前的任务一起执行的。而Promise的链式调用then,每次都会在内部生成一个新的Promise,然后执行then,在执行的过程中不断向微任务(microtask)推入新的函数,因此直至微任务(microtask)的队列清空后才会执行下一波的macrotask。
3. 炉火纯青版
这一个版本是上一个版本的进化版本,上一个版本的promise的then函数并未返回一个promise,如果在promise的then中创建一个promise,那么结果该如何呢?
new Promise((resolve,reject)=>{
console.log("promise1")
resolve()
}).then(()=>{ // p1
console.log("then11")
new Promise((resolve,reject)=>{
console.log("promise2")
resolve()
}).then(()=>{ // p2
console.log("then21")
}).then(()=>{ // p3
console.log("then23")
})
}).then(()=>{ // p4
console.log("then12")
})
// 输出
// promise1
// then11
// promise2
// then21
// then12
// then23
步骤
1. 主代码执行
console.log("promise1") ======================> promise1
微任务队列 = [p1]
2. 清空微任务队列
微任务队列 = []
console.log("then11") ======================> then11
console.log("promise2") ======================> promise2
微任务队列 = [p2 , p4]
3. 清空微任务队列
微任务队列 = [p4]
console.log("then21") ======================> then21
微任务队列 = []
console.log("then12") ======================> then12
微任务队列 = [p3]
4. 清空微任务队列
微任务队列 = []
console.log("then23") ======================> then23
3.1 变异版本
new Promise((resolve,reject)=>{
console.log("promise1")
resolve()
}).then(()=>{ // p1
console.log("then11")
return new Promise((resolve,reject)=>{
console.log("promise2")
resolve()
}).then(()=>{ // p2
console.log("then21")
}).then(()=>{ // p3
console.log("then23")
})
}).then(()=>{ // p4
console.log("then12")
})
// 输出
// promise1
// then11
// promise2
// then21
// then23
// then12
注意 return的Promise后,最外层的Promise的第二个then相当于挂在内部返回的Promise的最后一个then的返回值上。
3.2 变异版本2
new Promise((resolve,reject)=>{
console.log("promise1")
resolve()
}).then(()=>{ // p1
console.log("then11")
new Promise((resolve,reject)=>{
console.log("promise2")
resolve()
}).then(()=>{ // p2
console.log("then21")
}).then(()=>{ // p3
console.log("then23")
})
}).then(()=>{ // p4
console.log("then12")
})
new Promise((resolve,reject)=>{
console.log("promise3")
resolve()
}).then(()=>{ // p5
console.log("then31")
})
// 输出
// promise1
// promise3
// then11
// promise2
// then31
// then21
// then12
// then23
步骤:
1. 执行主代码
console.log("promise1") ======================> promise1
console.log("promise3") ======================> promise3
微任务队列 = [p1 , p5]
2. 清空微任务队列
微任务队列 = [p5]
console.log("then11") ======================> then11
console.log("promise2") ======================> promise2
微任务队列 = [p5 , p2]
微任务队列 = [p2 , p4]
console.log("then31") ======================> then31
微任务队列 = [p4 , p3]
console.log("then21") ======================> then21
微任务队列 = [p3]
console.log("then12") ======================> then12
微任务队列 = []
console.log("then23") ======================> then23
4. 登峰造极版
考点:在async/await之下,对Eventloop的影响。
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end"); // async (await返回一个promise,其后面的部分可以看做是被promise.then包裹)
}
async function async2() {
console.log( 'async2');
}
console.log("script start");
setTimeout(function () { // s1
console.log("settimeout");
},0);
async1();
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () { // p1
console.log("promise2");
});
console.log('script end');
// 输出
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// settimeout
步骤:
1. 执行主代码
console.log("script start"); ======================> script start
宏任务队列 = [s1]
console.log("async1 start"); ======================> async1 start
console.log( 'async2'); ======================> async2
微任务队列 = [async]
console.log("promise1"); ======================> promise1
微任务队列 = [async , p1]
console.log('script end'); ======================> script end
2. 清空微任务队列
微任务队列 = [p1]
console.log("async1 end"); ======================> async1 end
微任务队列 = []
console.log("promise2"); ======================> promise2
3. 拿出宏任务队列中的第一项 s1
宏任务队列 = []
console.log("settimeout"); ======================> settimeout
该宏任务执行完毕
async/await仅仅影响的是函数内的执行,而不会影响到函数体外的执行顺序。也就是说async1()并不会阻塞后续程序的执行,await async2()相当于一个Promise,console.log("async1 end");相当于前方Promise的then之后执行的函数。
4.1 async/await与promise的优先级详解
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end"); // async
}
async function async2() {
console.log( 'async2');
}
async1();
// 用于test的promise,看看await究竟在何时执行
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () { // p1
console.log("promise2");
}).then(function () { // p2
console.log("promise3");
}).then(function () { // p3
console.log("promise4");
}).then(function () { // p4
console.log("promise5");
});
// 输出
// async1 start
// async2
// promise1
// async1 end
// promise2
// promise3
// promise4
// promise5
步骤:
1. 执行主代码
console.log("async1 start"); ======================> async1 start
console.log( 'async2'); ======================> async2
微任务队列 = [async]
console.log("promise1"); ======================> promise1
微任务队列 = [async , p1]
2. 清空微任务队列
微任务队列 = [p1]
console.log("async1 end"); ======================> async1 end
微任务队列 = []
console.log("promise2"); ======================> promise2
微任务队列 = [p2]
微任务队列 = []
console.log("promise3"); ======================> promise3
微任务队列 = [p3]
console.log("promise4"); ======================> promise4
微任务队列 = [p4]
console.log("promise5"); ======================> promise5
5. 究极变态版
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");
});
setImmediate(()=>{
console.log("setImmediate")
})
process.nextTick(()=>{
console.log("process")
})
console.log('script end');
第一轮:
current task:"script start","async1 start",'async2',"promise1",“script end”
micro task queue:[async,promise.then,process]
macro task queue:[setTimeout,setImmediate]
第二轮
current task:process,async1 end ,promise.then
micro task queue:[]
macro task queue:[setTimeout,setImmediate]
第三轮
current task:setTimeout,setImmediate
micro task queue:[]
macro task queue:[]
最终结果:[script start,async1 start,async2,promise1,script end,process,async1 end,promise2,setTimeout,setImmediate]
同样"async1 end","promise2"之间的优先级,因平台而异。
* 干货总结
在处理一段EventLoop的时候
- 第一步确认宏任务,微任务
- 宏任务:
script,setTimeout,setImmediate,promise中的executor
- 微任务:
promise.then,process.nextTick
- 宏任务:
- 第二步解析“拦路虎”,出现
async/await
的时候不要慌,他们只是在标记的函数中作威作福,出了这个函数还是跟着大部队的潮流。 - 第三步,根据Promise的then调用方式的不同做出不同的判断,是链式调用还是分别调用。
- 最后一步,记住一些特别事件:
- 比如:
process.nextTick
优先级高于Promise.then
- 比如:
本文是手抄自该文章https://juejin.im/post/5c9a43175188252d876e5903,对题目做了自己的步骤备注,感谢作者!
网友评论