Асинхронный водопад, возвращающий ‘Обратный вызов уже был вызван’

#javascript #node.js #async.js

#javascript #node.js #async.js

Вопрос:

Я понимаю, почему я получаю эту ошибку, однако я понятия не имею, как это исправить. Вот как выглядит моя первая функция:

Все, что я делаю, это выполняю запрос get для всех моих URL, которые хранятся в базе данных, а затем для каждого ответа URL я просматриваю тело и сохраняю информацию о значениях, вызывая saveInformation(value) или saveInfoTwo(value)

 function getUrlInfo(urls, callback) {
    urls.map(function(url) {
        request.get(urls, {
            timeout: 1500
        }, function(error, response, body) {
            if (error) {console.log(error);
            } else {
                parseString(body, function(error, result) {
                    if (error) {console.log(error);
                    } else {
                        result.Values.forEach(function(listValues) {
                            listValues.forEach(function(value) {
                                saveInformation(value);
                            });
                            saveInfoTwo(value);
                        });
                    }
                });
            }
            callback(null);
        });
    });
}
  

Мой асинхронный водопад выглядит следующим образом:

 async.waterfall([
    getUrls,
    getUrlInfo
], function(err, result) {
    mongoose.connection.close();
});
  

Where getUrls — это просто метод, который ищет все URL-адреса и добавляет их в urls массив, чтобы getUrlInfo можно было его использовать.

 function getUrls(callback) {
    var urls = [];
    urlSchema.find(function(error, data) {
        data.forEach(function(result) {
            clusters.push(result.url);
        });
        callback(null, urls);
    });
}
  

Я использую mongodb в качестве своей базы данных.

Это отлично работает, когда есть только один URL. Как только я добавляю другой URL, я получаю эту ошибку:

 Error: Callback was already called.
  

Теперь, после попытки отладки, кажется, что все работает нормально для первого URL, а затем, когда второй URL завершает цикл foreach, выдается ошибка.

Как я могу это исправить?

Я почти уверен, что обратный вызов в моем getUrlInfo() ожидает завершения всех URL-адресов и циклов foreach, а затем продолжается? — я думаю, это не тот случай.

Любая помощь была бы оценена!

Подводя итог: как я могу предотвратить появление ошибки при добавлении другого URL?

Комментарии:

1. Ну, не использовать urls.map какой цикл, который будет вызываться callback несколько раз?

2. Вместо этого используйте async.map(urls, …, callback)

3. Не могли бы вы, пожалуйста, показать нам getUrls ? Вероятно, он вызывает свой обратный вызов более одного раза.

4. Не могли бы вы показать мне async.map с примером моего кода, пожалуйста? Я новичок в обратных вызовах. @Gil Z, сейчас обновляю ответ

Ответ №1:

Ваша карта вызывает функцию для первого URL, когда она завершается, callback вызывается. Когда выполняется второй, по завершении он будет вызываться callback снова, таким образом, у вас возникает эта ошибка.

Решение: дождитесь завершения всех URL-адресов, а затем вызовите обратный вызов.

Как сказал @Bergi в комментариях, вы можете использовать асинхронную карту

 function getUrlInfo(urls, callback) {
    async.map(urls, function(url, cb) {
      //do what you must
      cb();
    }, callback);
}