Node.js - Async - multiple innercallbacks within a if statement

孤街浪徒 提交于 2020-01-06 04:34:12

问题


I'm using Node.js and the async library, however I keep seeing the error: Callback was already called.

I think I understand why I get the error, however I don't know if it is actually possible to do the following/how can resolve.

Basically I want both the innercallbacks to have completed before the outercallback is completed.

So the code with which I am facing this issue looks like:

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);

Btw - I'm using setTimeout along with async.forEachLimit to reduce number of requests made to Azure (as I don't have too many)


回答1:


Promise can be used to ensure the order of asynchronized callbacks.

Take a look at async#AsyncFunction in the official document of async package

Wherever we accept a Node-style async function, we also directly accept an ES2017 async function. In this case, the async function will not be passed a final callback argument, and any thrown error will be used as the err argument of the implicit callback, and the return value will be used as the result value. (i.e. a rejected of the returned Promise becomes the err callback argument, and a resolved value becomes the result.)

The third argument of the async#forEachLimit is an async#AsyncFunction so you can just return a promise from it and later resolve the promise to indicate that its job has finished.

Your code can be improved as follows,

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);

Note that you should make sure you have Promise support in your Node environment. Builtin Promise is supported after Node v6. Check out here.

Updated

I've misunderstood you internal callbacks which both of them must have completed before outercallback is called. I've corrected it with Promise.all, which takes an array of Promise as arguments and return a Promise which will be resolved if all sub-Promises are all resolved or rejected if one of sub-Promises is rejected. See Promise#all.

Updated (2018.05.14)

You have to ensure that every innercallback received from AsyncFunction (i.e. the 3rd argument of async#forEachLimit) of the async package is called only once during each iteration. Especially to be careful when you do Array#forEach in each iteration since it may make you call innercallback multiple times in an iteration.

I've update the code block above, where I take away all calls to innercallback from the callback of client.execute and put all client.execute into a promise. After that I collect all promises into the asyncPromises array. Promise#all is used to ensure that all promises are resolved (i.e. all client.executes are finished) then finally call innercallback. Or if one of the promise above is rejected and is caught in Promise#catch, call innercallback with the first argument to be the error reason.




回答2:


You are calling innercallback twice in case both client.execute throw error. You can use async.parallel function or Promise.all, here is example of async.parallel and also you need to call innercallback function in else block

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);

Update: updated code, now using async.eachSeries in the place for Array.forEach



来源:https://stackoverflow.com/questions/50265790/node-js-async-multiple-innercallbacks-within-a-if-statement

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!