为什么对外层作用域变量使用内联await和普通await赋值结果不同?
核心原因
差异来自JS表达式的从左到右求值规则和await的暂停特性的共同作用,两个写法的求值顺序完全不同:
run1的写法output1 = output1 + (await sleep(sec)):会先读取当前output1的值,再进入await暂停等待异步返回,暂停期间其他并发任务修改的output1值,不会更新你已经读取到的左操作数run2的写法:会先await拿到异步返回值,再读取当前output2的最新值做计算,所以能正确累加
具体执行过程拆解
代码里的seconds.map(async ...)会同时启动3个并发的异步任务,两个函数的执行逻辑分别如下:
run1执行逻辑
3个任务启动时,output1初始值是0,每个任务第一步都会先读取output1的值为0,再进入await sleep各自等待对应的时间:
- 1000ms的任务先唤醒:用之前读取的
0 + 1000赋值给output1,此时output1 = 1000 - 2000ms的任务随后唤醒:用最开始读取的
0 + 2000赋值,覆盖之前的结果,此时output1 = 2000 - 3000ms的任务最后唤醒:用最开始读取的
0 + 3000赋值,覆盖之前的结果,最终output1 = 3000
run2执行逻辑
3个任务先await sleep拿到返回值,再读取当前最新的output2值做计算:
- 1000ms的任务先唤醒,拿到返回值1000,读取当前
output2 = 0,计算0+1000赋值,此时output2 = 1000 - 2000ms的任务随后唤醒,拿到返回值2000,读取当前
output2 = 1000,计算1000+2000赋值,此时output2 = 3000 - 3000ms的任务最后唤醒,拿到返回值3000,读取当前
output2 = 3000,计算3000+3000赋值,最终output2 = 6000
你可以直接运行以下代码验证结果:
"use strict"; function sleep(ms) { return new Promise(resolve => setTimeout(() => { resolve(ms); }, ms) ); } const seconds = [1000, 3000, 2000]; let output1 = 0; let output2 = 0; console.log("start"); (async function run1() { await Promise.all( seconds.map(async sec => { output1 = output1 + (await sleep(sec)); }) ); console.log({ output1 }); // 输出 { output1: 3000 } })(); (async function run2() { await Promise.all( seconds.map(async sec => { const res = await sleep(sec); output2 = output2 + res; }) ); console.log({ output2 }); // 输出 { output2: 6000 } })(); console.log("fin");
内容的提问来源于stack exchange,提问作者ikhvjs
相关产品推荐
相关产品推荐

