前言
之前我写过关于事件循环机制的文章,总的来说代码可以分为同步代码(不耗时)和异步代码(需要耗时) 异步代码又分为宏任务和微任务
- 异步代码:
-
宏任务队列:script,setTimeout,setInterval,setImmediate,I/O,UI渲染,requestAnimationFrame(下一帧渲染)
-
微任务队列:promise.then,process.nextTick,MutationObserver
-
事件循环机制有下面几个步骤:
- 执行同步代码(也叫宏任务)
- 执行微任务队列中的任务
- 有必要的话渲染
- 执行宏任务队列中的任务,开启下一次事件循环
console.log(1);
async function A() {
await B()
console.log(2);
}
async function B() {
console.log(3);
}
A()
setTimeout(() => {
console.log(4);
}, 0);
new Promise((resolve, reject) => {
console.log(5);
resolve()
})
.then(() => {
console.log(6);
})
.then(() => {
console.log(7);
})
console.log(8);
因此这段代码的输出顺序应该是这样的:第一行为同步代码直接输出1;定义了函数A和B,到第10行A函数调用,await B(),await会将B当同步代码执行,并将后续代码推入微任务队列,所以会打印3;到12行的定时器进入宏任务队列;16行是同步代码,会打印5;后续的两个.then是微任务进入微任务队列,26行的是同步任务打印8;同步代码执行完后按照微任务队列执行微任务,打印2,6,7;微任务执行完毕后按照宏任务队列执行宏任务打印4,同时开启下一次事件循环.因此结果是1,3,5,8,2,6,7,4
接下来就是恶心的题目了,希望大家能看懂后,遇到类似的问题不会被坑
var a
var b = new Promise((resolve) => {
console.log(1);
setTimeout(() => {
resolve(2)
}, 1000)
}).then(() => {
console.log(3);
}).then(() => {
console.log(4);
}).then(() => {
console.log(5);
})
a = new Promise(async (resolve) => {
console.log(a)
await b
console.log(a)
await (a)
resolve(true)
console.log(6);
})
console.log('end');
结果是这样的:
接下来我们好好说一说什么情况
首先第一行var a全局定义了一个a值为undefined,到了第三行打印1;定时器进入宏任务队列;后面三个.then进入微任务队列,到了16行打印a,因为15行给a赋值里的promise对象的回调函数还没执行完所以a还是undefined打印;await b后的代码进入微任务队列,24行打印end.开始执行微任务队列的微任务,所以打印3,4,5;第18行的打印a,因为15行给a赋值里的promise对象的回调函数执行完了所以会是一个状态为pending的promise对象.此时问题来了,为什么不会打印6呢?这就和async和await的一个机制有关了
-async/await
- async函数返回一个promise对象
- await后面的接的代码会作为同步代码执行
- await会将后续代码挤入微任务队列中
- await后面接的promise状态无法变更为fullfilled或rejected的话会发生阻塞(死锁),代码会一直停留在await上,出现这种情况await会让出主线程执行其他可行的任务
第17行的await b其实就发生了死锁情况,resolve(2)在定时器宏任务当中还没改变promsie状态,但因为别的线程还有其他任务所以await让出主线程执行其他可行的任务,因此await b会卡了1s执行宏任务计时器将promise状态进行变更,脱离卡死,继续执行微任务,到了await (a),同理应该让出主线程执行其他可行的任务,但此时已经没有其他任务了.所以会一直卡死,不会打印6.