由一道面试题引出:
//请写出输出内容
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');
/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/复制代码
知识点:
在理清楚以下几个知识点后上面问题就迎刃而解了:
1. 宏任务:包括script(整体代码),setTimeout,setInterval,I/O,UI交互,postMessage
2. 微任务:包括promise.then,MutationObserver
3. 执行过程: a) 执行一个宏任务 b)过程中遇到微任务,则添加其到微任务队列 c)当前宏任务执行完之后,开始依次执行所有微任务 d)GUI线程接管开始渲染 e)渲染完成交给js线程重复这个过程
4. async标志的函数里面,只有当await的表达式返回结果后,await以后的代码才会被执行
5. async/await其实是promise的语法糖,所以await以后的代码相当于放入了then里面
解题:
1. 代码开始,宏任务是script
Macro tasks
---------------
| script |
---------------复制代码
2. 从上往下,首先看到了同步代码log,则输出 script start
3. 遇到settimeout,将其里面的内容加入宏任务队列
Macro tasks
----------------------------------
| script | log 'setTimeout' |
----------------------------------复制代码
4. 运行async1函数,没遇到await之前都是同步代码,输出 async1 start
5. 遇到await,解析成如下形式
Promise.resolve(async2()).then(() => {console.log('async1 end')})复制代码
6. async2因为直接返回值无需等待,所以输出 async2
7. 如上改写可以看出那句log被放入了微任务队列
Macro tasks queue
----------------------------------
| script | log 'setTimeout' |
----------------------------------
Micro tasks queue
-----------------------
| log 'async1 end' |
-----------------------复制代码
8. 遇到new promise实例化过程,此过程依然是同步的,输出promise1
,并把then的内容加入微队列
Macro tasks queue
----------------------------------
| script | log 'setTimeout' |
----------------------------------
Micro tasks queue
-------------------------------------------
| log 'async1 end' | log 'promise2' |
-------------------------------------------复制代码
9. 输出同步log:script end
10. 此时已经到了script这个宏任务的最后了,开始执行其所有微任务,清空微任务队列,输出async1 end
,promise2
Macro tasks queue
----------------------------------
| script | log 'setTimeout' |
----------------------------------
Micro tasks queue
-------------------------------------------
| |
-------------------------------------------复制代码
11. script宏做完,自己出队列
Macro tasks queue
----------------------------------
| log 'setTimeout' |
----------------------------------
Micro tasks queue
-------------------------------------------
| |
-------------------------------------------复制代码
12. 浏览器开始渲染,渲染完成开始下一个宏任务
13. 输出setTimeout
, 清空宏队列,完成!
改版题:
这种题就算知道了解法,也推荐画图,不然很容易晕
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
//async2做出如下更改:
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise3');
resolve();
}).then(function() {
console.log('promise4');
});
console.log('script end');复制代码
// 答案
script start
async1 start
promise1
promise3
script end
promise2
async1 end
promise4
setTimeout复制代码
settimeout加入宏队列,promise1在实例化时输出,promise2和async1 end加入微队列
Macro tasks queue Micro tasks queue
--------------------------- ------------------------------
| script | setTimeout | | promise2 | async1 end |
--------------------------- ------------------------------
promise3输出,promise4加入微队列
------------------------- ---------------------------------------
| script | settimeout | | promise2 | async1 end | promise4 |
------------------------- ---------------------------------------复制代码
另一个
async function async1() {
console.log('async1 start');
await async2();
//更改如下:
setTimeout(function() {
console.log('setTimeout1')
},0)
}
async function async2() {
//更改如下:
setTimeout(function() {
console.log('setTimeout2')
},0)
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout3');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');复制代码
// 答案
script start
async1 start
promise1
script end
promise2
setTimeout3
setTimeout2
setTimeout1复制代码
settimeout3加入宏队列,settimeout2也加入宏,注意此时后面整体代码加入微队列
Macro tasks queue Micro tasks queue
------------------------------------------- ------------------------------
| script | setTimeout3 | settimeout2 | | setTiemout(settimout1) |
------------------------------------------- ------------------------------
promise3输出,promise4加入微队列
----------------------------------------- -----------------------------------------
| script | settimeout3 | settimeout2 | | setTimeout(settimeout1) | promise2 |
----------------------------------------- -----------------------------------------
script end输出后开始执行微队列,setTimoue被取出执行,把settimeout1加入宏队列
-------------------------------------------------- --------------
| script | setimeout3 | settimeou2 | settimeout1 | | promise2 |
-------------------------------------------------- -------------复制代码
另一个
async function a1 () {
console.log('a1 start')
await a2()
console.log('a1 end')
}
async function a2 () {
console.log('a2')
}
console.log('script start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve().then(() => {
console.log('promise1')
})
a1()
let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})
promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')复制代码
// 答案
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout复制代码
这跟上面差不多,只不过把promise2分开写,实例化是同步的所以先运行log promise2
原文: