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

Node.js使用async库遇Callback was already called错误求解决

解决async库「Callback was already called」错误 + 实现多内部回调完成后触发外部回调

嘿,我刚看完你的代码,立马就发现问题所在了——你现在的写法会多次调用innercallback,这就是触发「Callback was already called」错误的核心原因!咱们一步步拆解解决:

错误根源分析

你在client.execute的回调里直接调用了innercallback,而且每个odId下有两个client.execute,再加上外层的myJson.matches和oMatches.ids循环,相当于一个data项会触发N次innercallback调用。但async的forEachLimit要求每个迭代(也就是每个data)只能调用一次innercallback,多调用就会报错。

实现需求的正确姿势

你的需求是「两个内部回调全部完成后再执行外部回调」,更准确地说,是每个data对应的所有异步操作(所有匹配到的odId下的两个client.execute)全部完成后,才调用一次innercallback。咱们可以用async的each和parallel来嵌套管理这些异步任务:

修改后的代码

async.forEachLimit(inData, 25, function (data, innercallback) {
  // 用async.each遍历myJson.matches,替代普通forEach(因为内部有异步操作)
  async.each(myJson.matches, function (oMatches, matchCallback) {
    if (data.$.id == oMatches.SourceId) {
      // 遍历当前oMatches下的所有ids
      async.each(oMatches.ids, function (odId, idCallback) {
        // 用async.parallel并行执行两个client.execute操作
        async.parallel([
          function (taskCallback) {
            client.execute(
              `g.addV('test').property('id', "${data.$.id}")`,
              {},
              function (err) {
                setTimeout(function () {
                  taskCallback(err);
                }, 2000);
              }
            );
          },
          function (taskCallback) {
            client.execute(
              `g.V('${data.$.id}').addE('matches').to(g.V('xyz'))`,
              {},
              function (err) {
                setTimeout(function () {
                  taskCallback(err);
                }, 2000);
              }
            );
          }
        ], function (err) {
          // 两个并行任务完成后,触发idCallback,继续下一个odId
          idCallback(err);
        });
      }, function (err) {
        // 当前oMatches下的所有ids处理完成,触发matchCallback
        matchCallback(err);
      });
    } else {
      // 没有匹配的话,直接触发matchCallback,继续下一个oMatches
      matchCallback(null);
    }
  }, function (err) {
    // 所有myJson.matches处理完成,才调用innercallback(每个data只调用一次!)
    innercallback(err);
  });
}, outercallback);

关键说明

  1. 避免重复调用回调:每个层级的异步任务都用对应的async方法管理,确保只有当该层级所有操作完成后,才调用下一层级的回调,最终每个data只会调用一次innercallback。
  2. 并发控制:外层的forEachLimit(inData, 25)已经控制了同时处理25个data,内部的parallel让每个odId下的两个请求并行执行,既满足Azure的配额限制,又保证效率。
  3. setTimeout的使用:你加的2000ms延迟可以有效降低请求频率,不过如果担心延迟累积,也可以考虑把setTimeout放在client.execute调用之前,或者用async.retry处理可能的配额超限问题。

额外小优化

可以用模板字符串(反引号`)来拼接Gremlin语句,比字符串拼接更清晰,也减少转义错误的概率,我在代码里已经帮你改了~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:49:00