你不知道的 async、await 魔鬼细节

📁 36563688 📅 2026-07-20 11:59:47 👤 admin 👁️ 6703 ❤️ 517
你不知道的 async、await 魔鬼细节

}

asyncfunctionasync2{

returnnewPromise( ( resolve, reject) => {

resolve

})

}

async1

newPromise( ( resolve) => {

console.log( 'B')

resolve

}).then( => {

console.log( 'C')

}).then( => {

console.log( 'D')

})

// 最终结果👉: B C D A

❓基本一样的代码为什么会出现差别,话不多说👇

1、async 函数返回值

在讨论 await之前,先聊一下 async函数处理返回值的问题,它会像 Promise.prototype.then一样,会对返回值的类型进行辨识。

👉 根据返回值的类型,引起 js引擎 对返回值处理方式的不同

📑结论: async函数在抛出返回值时,会根据返回值 类型开启 不同数目的微任务

return结果值:非thenable、非promise(不等待)

return结果值:thenable(等待 1个then的时间)

return结果值:promise(等待 2个then的时间)

🌰1:

asyncfunctiontestA{

return1;

}

testA.then( => console.log( 1));

Promise.resolve

.then( => console.log( 2))

.then( => console.log( 3));

// (不等待)最终结果👉: 1 2 3

🌰2:

asyncfunctiontestB{

return{

then (cb) {

cb;

}

};

}

testB.then( => console.log( 1));

Promise.resolve

.then( => console.log( 2))

.then( => console.log( 3));

// (等待一个then)最终结果👉: 2 1 3

🌰3:

asyncfunctiontestC{

returnnewPromise( ( resolve, reject) => {

resolve

})

}

testC.then( => console.log( 1));

Promise.resolve

.then( => console.log( 2))

.then( => console.log( 3));

// (等待两个then)最终结果👉: 2 3 1

asyncfunctiontestC{

returnnewPromise( ( resolve, reject) => {

resolve

})

}

testC.then( => console.log( 1));

Promise.resolve

.then( => console.log( 2))

.then( => console.log( 3))

.then( => console.log( 4))

// (等待两个then)最终结果👉: 2 3 1 4

看了这三个🌰是不是对上面的结论有了更深的认识?

稍安勿躁,来试试一个经典面试题👇

asyncfunctionasync1{

console.log( '1')

awaitasync2

console.log( 'AAA')

}

asyncfunctionasync2{

console.log( '3')

returnnewPromise( ( resolve, reject) => {

resolve

console.log( '4')

})

}

console.log( '5')

setTimeout( => {

console.log( '6')

}, 0);

async1

newPromise( ( resolve) => {

console.log( '7')

resolve

}).then( => {

console.log( '8')

}).then( => {

console.log( '9')

}).then( => {

console.log( '10')

})

console.log( '11')

// 最终结果👉: 5 1 3 4 7 11 8 9 AAA 10 6

👀做错了吧?

哈哈没关系

步骤拆分👇:

先执行同步代码,输出 5

执行 setTimeout,是放入宏任务异步队列中

接着执行 async1函数,输出 1

执行 async2函数,输出 3

Promise构造器中代码属于同步代码,输出 4

async2函数的返回值是 Promise,等待 2个 then后放行,所以 AAA暂时无法输出

async1函数 暂时结束,继续往下走,输出 7

同步代码,输出 11

执行第一个 then,输出 8

执行第二个 then,输出 9

终于 等到了两个 then执行完毕,执行 async1函数里面剩下的,输出 AAA

再执行最后一个微任务 then,输出 10

执行最后的宏任务 setTimeout,输出 6

❓是不是豁然开朗,欢迎点赞收藏!

2、await 右值类型区别2.1、非 thenable

🌰1:

asyncfunctiontest{

console.log( 1);

await1;

console.log( 2);

}

test;

console.log( 3);

// 最终结果👉: 1 3 2

🌰2:

functionfunc{

console.log( 2);

}

asyncfunctiontest{

console.log( 1);

awaitfunc;

console.log( 3);

}

test;

console.log( 4);

// 最终结果👉: 1 2 4 3

🌰3:

asyncfunctiontest{

console.log( 1);

await123

console.log( 2);

}

test;

console.log( 3);

Promise.resolve

.then( => console.log( 4))

.then( => console.log( 5))

.then( => console.log( 6))

.then( => console.log( 7));

// 最终结果👉: 1 3 2 4 5 6 7

Note:

await后面接非 thenable类型,会立即向微任务队列添加一个微任务 then, 但不需等待

console.log( 1);

await{

then (cb) {

cb;

},

};

console.log( 2);

}

test;

console.log( 3);

Promise.resolve

.then( => console.log( 4))

.then( => console.log( 5))

.then( => console.log( 6))

.then( => console.log( 7));

// 最终结果👉: 1 3 4 2 5 6 7

Note:

await后面接 thenable类型,需要 等待一个 then 的时间之后执行

console.log( 1);

awaitnewPromise( ( resolve, reject) => {

resolve

})

console.log( 2);

}

test;

console.log( 3);

Promise.resolve

.then( => console.log( 4))

.then( => console.log( 5))

.then( => console.log( 6))

.then( => console.log( 7));

// 最终结果👉: 1 3 2 4 5 6 7

❓为什么表现的和非 thenable值一样呢?为什么不等待两个 then的时间呢?

Note:

TC 39(ECMA标准制定者) 对await后面是 promise的情况如何处理进行了一次修改, 移除了额外的两个微任务,在 早期版本,依然会等待两个 then的时间

有大佬翻译了官方解释: 更快的 async 函数和 promises[1],但在这次更新中并没有修改 thenable的情况

这样做可以极大的优化 await等待的速度👇

asyncfunctionfunc{

console.log( 1);

await1;

console.log( 2);

await2;

console.log( 3);

await3;

console.log( 4);

}

asyncfunctiontest{

console.log( 5);

awaitfunc;

console.log( 6);

}

test;

console.log( 7);

Promise.resolve

.then( => console.log( 8))

.then( => console.log( 9))

.then( => console.log( 10))

.then( => console.log( 11));

// 最终结果👉: 5 1 7 2 8 3 9 4 10 6 11

Note:

await和 Promise.prototype.then虽然很多时候可以在 时间顺序上能等效,但是它们之间有 本质的区别。

test函数中的 await会等待 func函数中所有的 await取得 恢复函数执行 的命令并且整个函数执行完毕后才能获得取得 恢复函数执行的命令;

也就是说,func函数的 await此时 不能在时间的顺序上等效then,而要等待到 test函数完全执行完毕;

比如这里的数字6很晚才输出, 如果单纯看成 then的话,在下一个微任务队列执行时6就应该作为同步代码输出了才对。

所以我们可以合并两个函数的代码👇

asyncfunctiontest{

console.log( 5);

console.log( 1);

await1;

console.log( 2);

await2;

console.log( 3);

await3;

console.log( 4);

awaitnull;

console.log( 6);

}

test;

console.log( 7);

Promise.resolve

.then( => console.log( 8))

.then( => console.log( 9))

.then( => console.log( 10))

.then( => console.log( 11));

// 最终结果👉: 5 1 7 2 8 3 9 4 10 6 11

因为将原本的函数融合,此时的 await可以等效为 Promise.prototype.then,又完全可以等效如下代码👇

asyncfunctiontest{

console.log( 5);

console.log( 1);

Promise.resolve

.then( => console.log( 2))

.then( => console.log( 3))

.then( => console.log( 4))

.then( => console.log( 6))

}

test;

console.log( 7);

Promise.resolve

.then( => console.log( 8))

.then( => console.log( 9))

.then( => console.log( 10))

.then( => console.log( 11));

// 最终结果👉: 5 1 7 2 8 3 9 4 10 6 11

以上三种写法在时间的顺序上完全等效,所以你 完全可以将 await 后面的代码可以看做在 then 里面执行的结果,又因为 async函数会返回 promise实例,所以还可以等效成👇

asyncfunctiontest{

console.log( 5);

console.log( 1);

}

test

.then( => console.log( 2))

.then( => console.log( 3))

.then( => console.log( 4))

.then( => console.log( 6))

console.log( 7);

Promise.resolve

.then( => console.log( 8))

.then( => console.log( 9))

.then( => console.log( 10))

.then( => console.log( 11));

// 最终结果👉: 5 1 7 2 8 3 9 4 10 6 11

可以发现,test函数全是走的同步代码...

所以👉:**async/await是用同步的方式,执行异步操作**

3、🌰

🌰1:

asyncfunctionasync2{

newPromise( ( resolve, reject) => {

resolve

})

}

asyncfunctionasync3{

returnnewPromise( ( resolve, reject) => {

resolve

})

}

asyncfunctionasync1{

// 方式一:最终结果:B A C D

// await new Promise((resolve, reject) => {

// resolve

// })

// 方式二:最终结果:B A C D

// await async2

// 方式三:最终结果:B C D A

awaitasync3

console.log( 'A')

}

async1

newPromise( ( resolve) => {

console.log( 'B')

resolve

}).then( => {

console.log( 'C')

}).then( => {

console.log( 'D')

})

大致思路👇:

首先,**async函数的整体返回值永远都是Promise,无论值本身是什么**

方式一:await的是Promise,无需等待

方式二:await的是async函数,但是该函数的返回值本身是**非thenable**,无需等待

方式三:await的是async函数,且返回值本身是Promise,需等待两个then时间

🌰2:

function func {

console.log(2);

// 方式一:1 2 4 5 3 6 7

// Promise.resolve

// .then( => console.log(5))

// .then( => console.log(6))

// .then( => console.log(7))

// 方式二:1 2 4 5 6 7 3

return Promise.resolve

.then( => console.log(5))

.then( => console.log(6))

.then( => console.log(7))

}

async function test {

console.log(1);

await func;

console.log(3);

}

test;

console.log(4);

步骤拆分👇:

方式一:

同步代码输出1、2,接着将log(5)处的then1加入微任务队列,await拿到确切的func函数返回值undefined,将后续代码放入微任务队列(then2,可以这样理解)

执行同步代码输出4,到此,所有同步代码完毕

执行第一个放入的微任务then1输出5,产生log(6)的微任务then3

执行第二个放入的微任务then2输出3

然后执行微任务then3,输出6,产生log(7)的微任务then4

执行then4,输出7

方式二:

同步代码输出1、2,await拿到func函数返回值,但是并未获得 具体的结果(由 Promise本身机制决定),暂停执行当前async函数内的代码(跳出、让行)

输出4,到此,所有同步代码完毕

await一直等到Promise.resolve.then...执行完成,再放行输出3

方式二没太明白❓

继续👇

function func {

console.log(2);

return Promise.resolve

.then( => console.log(5))

.then( => console.log(6))

.then( => console.log(7))

}

async function test {

console.log(1);

await func

console.log(3);

}

test;

console.log(4);

new Promise((resolve) => {

console.log('B')

resolve

}).then( => {

console.log('C')

}).then( => {

console.log('D')

})

// 最终结果👉: 1 2 4 B 5 C 6 D 7 3

还是没懂?

继续👇

asyncfunctiontest{

console.log( 1);

awaitPromise.resolve

.then( => console.log( 5))

.then( => console.log( 6))

.then( => console.log( 7))

console.log( 3);

}

test;

console.log( 4);

newPromise( ( resolve) => {

console.log( 'B')

resolve

}).then( => {

console.log( 'C')

}).then( => {

console.log( 'D')

})

// 最终结果👉: 1 4 B 5 C 6 D 7 3

Note:

综上, await一定要等到右侧的表达式有 确切的值才会放行,否则将一直等待(阻塞当前 async函数内的后续代码),不服看看这个👇

functionfunc{

returnnewPromise( ( resolve) => {

console.log( 'B')

// resolve 故意一直保持pending

})

}

asyncfunctiontest{

console.log( 1);

awaitfunc

console.log( 3);

}

test;

console.log( 4);

// 最终结果👉: 1 B 4 (永远不会打印3)

// ---------------------或者写为👇-------------------

asyncfunctiontest{

console.log( 1);

awaitnewPromise( ( resolve) => {

console.log( 'B')

// resolve 故意一直保持pending

})

console.log( 3);

}

test;

console.log( 4);

// 最终结果👉: 1 B 4 (永远不会打印3)

🌰3:

asyncfunctionfunc{

console.log( 2);

return{

then (cb) {

cb

}

}

}

asyncfunctiontest{

console.log( 1);

awaitfunc;

console.log( 3);

}

test;

console.log( 4);

newPromise( ( resolve) => {

console.log( 'B')

resolve

}) .then( => {

console.log( 'C')

}).then( => {

console.log( 'D')

})

// 最终结果👉: 1 2 4 B C 3 D

步骤拆分👇:

同步代码输出1、2

await拿到func函数的具体返回值thenable,将当前async函数内的后续代码放入微任务then1(但是需要等待一个then时间)

同步代码输出4、B,产生log(C)的微任务then2

由于then1滞后一个then时间,直接执行then2输出C,产生log(D)的微任务then3

执行原本滞后一个then时间的微任务then1,输出3

执行最后一个微任务then3输出D

async函数返回值

📑结论: async函数在抛出返回值时,会根据返回值 类型开启 不同数目的微任务

return结果值:非thenable、非promise(不等待)

return结果值:thenable(等待 1个then的时间)

return结果值:promise(等待 2个then的时间)

await右值类型区别

接非 thenable类型,会立即向微任务队列添加一个微任务 then, 但不需等待

接 thenable类型,需要 等待一个 then 的时间之后执行

接 Promise类型(有确定的返回值),会立即向微任务队列添加一个微任务 then, 但不需等待

TC 39 对await后面是 promise的情况如何处理进行了一次修改, 移除了额外的两个微任务,在 早期版本,依然会等待两个 then的时间

[1]返回搜狐,查看更多

相关推荐