Node.js - Async - несколько внутренних обратных вызовов в операторе if - PullRequest
0 голосов
/ 10 мая 2018

Я использую Node.js и асинхронную библиотеку, однако постоянно вижу ошибку: Callback was already called.

Мне кажется, я понимаю, почему я получаю ошибку, однако я не знаю, возможно ли на самом деле сделать следующее / как можно решить.

По сути, я хочу, чтобы оба внутренних вызова завершились до завершения внешнего вызова.

Итак, код, с которым я столкнулся с этой проблемой, выглядит следующим образом:

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 (поскольку у меня их не так много)

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Вы звоните innercallback дважды, если оба client.execute сгенерировали ошибку. Вы можете использовать async.parallel функцию или Promise.all, вот пример async.parallel, а также вам нужно вызвать innercallback функцию в else блоке

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 вместо Array.forEach

0 голосов
/ 10 мая 2018

Promise может использоваться для обеспечения порядка асинхронных обратных вызовов.

Взгляните на async#AsyncFunction в официальном документе async пакет

Где бы мы ни принимали асинхронную функцию в стиле узла, мы также напрямую принять асинхронную функцию ES2017. В этом случае асинхронная функция будет не будет передан окончательный аргумент обратного вызова, и любая брошенная ошибка будет используется в качестве аргумента err неявного обратного вызова и возврата значение будет использоваться в качестве значения результата. (то есть отклонено возвращаемое Promise становится ошибочным аргументом обратного вызова и разрешенным значение становится результатом.)

Третий аргумент async#forEachLimit - это async#AsyncFunction, так что вы можете просто вернуть ему обещание и позже разрешить обещание, чтобы указать, что его работа завершена.

Ваш код может быть улучшен следующим образом,

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 в вашей среде Node. Встроенный Promise поддерживается после Node v6. Проверьте здесь .

Обновлено

Я неправильно понял ваши внутренние обратные вызовы, которые они оба должны были завершить до вызова externalcallback. Я исправил это с помощью Promise.all, который принимает массив Promise в качестве аргументов и возвращает Promise, который будет разрешен, если все подпункты Promise все разрешены или отклонены, если один из подпунктов Promise s отклонено. См. Promise#all.

Обновлено (2018.05.14)

Необходимо убедиться, что каждый innercallback, полученный от AsyncFunction (т. Е. 3-й аргумент async#forEachLimit) пакета async, вызывается только один раз во время каждой итерации. Особенно будьте осторожны, когда вы делаете Array#forEach на каждой итерации, так как это может заставить вас вызывать innercallback несколько раз за итерацию.

Я обновил блок кода выше, где я забираю все вызовы innercallback из обратного вызова client.execute и помещаю все client.execute в обещание. После этого я собираю все обещания в массив asyncPromises. Promise#all используется, чтобы гарантировать, что все обещания разрешены (то есть все client.execute s закончены), затем, наконец, вызовите innercallback. Или, если одно из приведенных выше обещаний отклонено и перехвачено в Promise#catch, вызовите innercallback с первым аргументом, который будет причиной ошибки.

...