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

JavaScript Promise.all并行场景下push正常concat丢失元素原因求解

问题核心原因

本质是异步并行任务的竞态条件 + 不可变操作的赋值非原子性共同导致的:
Promise.all传入的多个异步回调是并行执行的,await之后的代码执行时机完全随机,两个操作会争抢共享变量employments的读写权。

版本1(push)正常的原因

Array.push()是对原数组直接修改的可变操作:

  • 所有异步任务操作的都是同一个内存地址的数组,不管哪个任务先执行追加操作,都会在已有元素的基础上新增内容,不会出现数据覆盖丢失的情况。

版本2(concat)异常的原因

Array.concat()是不可变操作,不会修改原数组,而是返回全新的数组,你写的employments = employments.concat(xxx)实际包含三个步骤:

  1. 读取当前employments的最新值
  2. 拼接新元素生成新数组
  3. 把新数组赋值给employments变量
    这三个步骤不是原子操作,多个并行任务很容易出现读写冲突,举个实际的冲突场景:

初始时employments = []
任务1和任务2先后执行到await mergeWithOtherSource处暂停,都已经读取到employments的初始值为[]
任务2的await先返回,拼接后得到新数组[a,b]赋值给employments
任务1的await随后返回,用之前读取到的旧值[]拼接得到[c],直接覆盖了任务2刚赋值的结果
最终employments只剩下[c],数据丢失,这就是你遇到随机返回1-3个元素的原因。

修复方案

两种可靠实现:

  • 继续使用版本1的push写法,逻辑最简单性能也最高
  • 如果你需要严格的不可变操作,不要在并行任务中直接修改共享变量,先收集所有结果再一次性合并:
let companyIds = ['Id1', 'Id2']
// 先收集所有异步任务的返回结果
const employmentChunks = await Promise.all(
    companyIds.map( async ( companyId ) => {
        const companies = await getCompaniesWithId(companyId);
        return mergeWithOtherSource( companies );
    } )
);
// 最后一次性合并所有结果
const employments = employmentChunks.flat();
return employments.length

内容的提问来源于stack exchange,提问作者Jeremias Nater

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 21:36:00