Node.js-Async-if语句中的多个InnerCallback

Node.js-Async-if语句中的多个InnerCallback,node.js,azure-cosmosdb,gremlin,async.js,Node.js,Azure Cosmosdb,Gremlin,Async.js,我正在使用Node.js和异步库,但是我一直看到错误:回调已被调用 我想我理解我为什么会出错,但是我不知道是否真的可以做到以下几点/如何解决 基本上,我希望在完成outercallback之前完成两个InnerCallback 因此,我面临这个问题的代码如下所示: async.forEachLimit(inData, 25, function (data, innercallback) { myJson.matches.forEach(function (oMatches) {

我正在使用Node.js和异步库,但是我一直看到错误:
回调已被调用

我想我理解我为什么会出错,但是我不知道是否真的可以做到以下几点/如何解决

基本上,我希望在完成outercallback之前完成两个InnerCallback

因此,我面临这个问题的代码如下所示:

async.forEachLimit(inData, 25, function (data, innercallback) {

    myJson.matches.forEach(function (oMatches) {
        if (data.$.id == oMatches.SourceId) {

            oMatches.ids.forEach(function (odId) {
                client.execute("g.addV('test').property('id', \"" + data.$.id + "\")", {},
                    function (err) {
                        setTimeout(function () { innercallback(err) }, 2000);
                    });


                client.execute("g.V('" + data.$.id + "').addE('matches').to(g.V('xyz'))", {},
                    function (err) {
                        setTimeout(function () { innercallback(err) }, 2000);
                    });
            })

        } //there is no else case.

    });

}, outercallback);
顺便说一句,我正在使用
setTimeout
async.forEachLimit
来减少对Azure的请求数量(因为我没有太多)

可以用来确保异步回调的顺序

看看 异步包的

无论我们在哪里接受节点样式的异步函数,我们也直接 接受ES2017异步函数。在这种情况下,异步函数将 不会传递最终回调参数,任何抛出的错误都将 用作隐式回调的err参数,并返回 值将用作结果值。(即被拒绝的 返回的承诺将成为err回调参数,并且 值成为结果。)

的第三个参数是一个
async#async函数
,因此您只需从它返回一个承诺,然后解析该承诺,以指示其工作已完成

您的代码可以改进如下:

async.forEachLimit(inData, 25, function (data, innercallback) {

  // we will wait for all async function in here to complete
  // then call the innercallback
  var asyncPromises = []

  myJson.matches.forEach(function (oMatches) {

    if (data.$.id == oMatches.SourceId) {

      oMatches.ids.forEach(function (odId) {
        asyncPromises.push(new Promise(function (resolve, reject) {
          client.execute("g.addV('test').property('id', \"" + data.$.id + "\")", {},
            function (err) {
              setTimeout(function () {
                if (err) {
                  reject(err)
                  return
                }
                resolve();
              }, 2000);
            });
        }))

        asyncPromises.push(new Promise(function (resolve, reject) {
          client.execute("g.V('" + data.$.id + "').addE('matches').to(g.V('xyz'))", {},
            function (err) {
              setTimeout(function () {
                if (err) {
                  reject(err)
                  return
                }
                resolve();
              }, 2000);
            });
        }))

      })

    } //there is no else case.

  })

  // Here we can ensure that innercallback is called only for once
  Promise.all(asyncPromises)
    .then(function () {
      innercallback(null)
    })
    .catch(function (err) {
      // handle the error here
      innercallback(err)
    })

}, outercallback);
请注意,您应该确保节点环境中有
Promise
支持。节点v6之后支持内置
Promise
。退房

已更新

我误解了您的内部回调,它们必须在调用outercallback之前完成。我已经用
Promise.all
对其进行了更正,它将
Promise
数组作为参数,并返回一个
Promise
,如果所有子
Promise
都已解决,则该问题将得到解决;如果其中一个子
Promise
被拒绝,则该问题将被拒绝。看

更新(2018.05.14)

您必须确保从AsyncFunction(即,
async#forEachLimit
包的
innercallback
的第三个参数)接收到的每个
async
包在每次迭代期间只调用一次。尤其是在每次迭代中执行
Array#forEach
时要小心,因为它可能会使您在迭代中多次调用
innercallback

我已经更新了上面的代码块,从
client.execute
的回调中删除对
innercallback
的所有调用,并将所有
client.execute
放入一个承诺中。之后,我将所有承诺收集到
asyncPromissions
数组中。
Promise#all
用于确保所有承诺都已解决(即所有
client.execute
s已完成),然后最后调用
innercallback
。或者,如果上面的一个承诺被拒绝,并且在
promise#catch
中被捕获,则调用
innercallback
,第一个参数作为错误原因。

如果两个
客户端都出现错误,则调用
innercallback
两次。执行
抛出错误。您可以使用function或
Promise.all
,这里是
async.parallel
的示例,您还需要在
else
块中调用
innercallback
函数

async.forEachLimit(inData, 25, function(data, innercallback) {
    async.eachSeries(myJson.matches, function(oMatches, callback) {
        if (data.$.id == oMatches.SourceId) {
            async.eachSeries(oMatches.ids, function(odId, callback) {
                async.parallel([
                    function(callback) {
                        client.execute("g.addV('test').property('id', \"" + data.$.id + "\")", {}, callback);
                    },
                    function(callback) {
                        client.execute("g.V('" + data.$.id + "').addE('matches').to(g.V('xyz'))", {}, callback);
                    }
                ], callback);
            }, callback)

        } else {
            callback();
        }
    }, innercallback);
}, outercallback);

更新:更新了代码,现在在
数组的位置使用
async.eachSeries
。forEach

尝试一下?我已经更新了代码。如果有任何问题,请告诉我。这不起作用-仍然获取已调用的
回调
errorError-RangeError:最大调用堆栈大小exceeded@userMod2
callback()
->
process.nextTick(函数(){callback()})
@SergeyLapin谢谢,这样就消除了最大调用堆栈大小错误-但是它实际上运行的是
g.addV('test')
第二行gremlinquery@SergeyLapin-我不能做类似
innercallback
innercallback2
的事情吗?哦,我明白了。每次迭代中的
innercallback
只能调用一次。我会为此更新代码。