You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么对外层作用域变量使用内联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各自等待对应的时间:

  1. 1000ms的任务先唤醒:用之前读取的0 + 1000赋值给output1,此时output1 = 1000
  2. 2000ms的任务随后唤醒:用最开始读取的0 + 2000赋值,覆盖之前的结果,此时output1 = 2000
  3. 3000ms的任务最后唤醒:用最开始读取的0 + 3000赋值,覆盖之前的结果,最终output1 = 3000

run2执行逻辑

3个任务先await sleep拿到返回值,再读取当前最新的output2值做计算:

  1. 1000ms的任务先唤醒,拿到返回值1000,读取当前output2 = 0,计算0+1000赋值,此时output2 = 1000
  2. 2000ms的任务随后唤醒,拿到返回值2000,读取当前output2 = 1000,计算1000+2000赋值,此时output2 = 3000
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 01:09:02