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

多次调用相同Async Await函数仅触发最后一次的问题排查

异步函数并发执行异常:原因分析与解决

问题代码

const fun = (x) => {
  console.log(`Meth... ${x}`);
  setTimeout(() => {
    onReceive(`Fun Received: ${x}`);
  }, 100);
  return x;
}

const publish = () => {
  return new Promise(resolve => {
    window.onReceive = (token) => {
      resolve(token);
    }
  });
}

const Inst = () => {
  const getAs = async(x) => {
    console.log(`Getting As ----- ${x}`);

    const res = await publish(fun(x));

    console.log(`Res is '${res}' ---- ${x}`);
  }

  return {
    getAs,
  }
}

const inst = Object.freeze(Inst());

const a = async() => {
  await inst.getAs(1);
  await inst.getAs(2);
  await inst.getAs(3);
  await inst.getAs(4);
  await inst.getAs(5);
};
a();

const b = async() => {
  await inst.getAs(4);
  await inst.getAs(5);
  await inst.getAs(6);
  await inst.getAs(7);
};
b();
// Comment or remove `b()` to see the different result in Console

现象

同时调用a()和b()时,仅b()中的inst.getAs()能正常执行,a()的代码完全停滞;注释或移除b()后,a()的所有inst.getAs()均可正常运行。

原因分析

核心问题在于全局变量window.onReceive被频繁覆盖:

  • 每次调用publish函数,都会把window.onReceive替换为当前Promise的resolve函数。
  • 当a()和b()同时启动,a()先调用getAs(1),给window.onReceive赋值了对应Promise的resolve;但紧接着b()调用getAs(4),直接把window.onReceive换成了自己的resolve。
  • 100ms后,fun(1)的setTimeout触发,调用的是被b()覆盖后的onReceive,这会resolveb()中getAs(4)的Promise,让b()的流程继续;而a()中getAs(1)的Promise永远处于pending状态,后续的await全部卡住,导致a()的代码无法继续执行。

解决方法

方案1:直接绑定Promise的resolve到当前调用(推荐)

改造fun和publish,让它们通过局部回调传递,不再依赖全局变量:

const fun = (x, callback) => {
  console.log(`Meth... ${x}`);
  setTimeout(() => {
    callback(`Fun Received: ${x}`);
  }, 100);
  return x;
}

const publish = (x) => {
  return new Promise(resolve => {
    fun(x, resolve); // 将当前Promise的resolve作为回调传给fun
  });
}

const Inst = () => {
  const getAs = async(x) => {
    console.log(`Getting As ----- ${x}`);

    const res = await publish(x);

    console.log(`Res is '${res}' ---- ${x}`);
  }

  return {
    getAs,
  }
}

const inst = Object.freeze(Inst());

const a = async() => {
  await inst.getAs(1);
  await inst.getAs(2);
  await inst.getAs(3);
  await inst.getAs(4);
  await inst.getAs(5);
};
a();

const b = async() => {
  await inst.getAs(4);
  await inst.getAs(5);
  await inst.getAs(6);
  await inst.getAs(7);
};
b();

方案2:用唯一标识符管理回调(适合无法修改fun的场景)

如果不能修改fun的结构,可通过Map存储多个回调,避免全局覆盖:

const callbackMap = new Map();
let callbackId = 0;

const fun = (x) => {
  console.log(`Meth... ${x}`);
  const id = callbackId++;
  setTimeout(() => {
    const callback = callbackMap.get(id);
    if (callback) {
      callback(`Fun Received: ${x}`);
      callbackMap.delete(id); // 执行后清理,防止内存泄漏
    }
  }, 100);
  return id; // 返回唯一ID,用于绑定回调
}

const publish = () => {
  return new Promise(resolve => {
    const id = fun();
    callbackMap.set(id, resolve); // 将当前resolve与ID绑定
  });
}

// 以下Inst、a、b函数保持原代码不变
const Inst = () => {
  const getAs = async(x) => {
    console.log(`Getting As ----- ${x}`);

    const res = await publish(fun(x));

    console.log(`Res is '${res}' ---- ${x}`);
  }

  return {
    getAs,
  }
}

const inst = Object.freeze(Inst());

const a = async() => {
  await inst.getAs(1);
  await inst.getAs(2);
  await inst.getAs(3);
  await inst.getAs(4);
  await inst.getAs(5);
};
a();

const b = async() => {
  await inst.getAs(4);
  await inst.getAs(5);
  await inst.getAs(6);
  await inst.getAs(7);
};
b();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 14:57:21