Подтвердить что ты не робот

Lodash: _.forEach с функцией

Я пытаюсь использовать метод lodash forEach с вложенной функцией, которая вызывает базу данных mongo.

var jobs = [];
_.forEach(ids, function(id) {
    JobRequest.findByJobId(id, function(err, result) {
        if(err) callback(err);
        jobs.push(result);
    });
});

callback(null, jobs);

У меня возникают проблемы, потому что forEach и callback будут работать до того, как будет вызвана внутренняя функция. Как я могу это решить?

Я хочу, чтобы вызываемый вызов вызывался после завершения каждой и внутренней функции.

4b9b3361

Ответ 1

Еще один подход заключается в том, чтобы обернуть все в promises, в этом случае результаты задания будут перенесены в массив в правильном порядке:

var promises = ids.map(function(id) {
    return new Promise(function(resolve, reject) {
        JobRequest.findByJobId(id, function (err, result) {
            if (err) reject(err);
            resolve(result);
        });
    });
});

Promise.all(promises).then(function(jobs) {
    callback(null, jobs);
}, callback);

// or shorter: Promise.all(promises).then(callback.bind(null, null), callback);

Обратите внимание, что вам также необходимо обрабатывать потенциальную ситуацию, когда запрос JobRequest.findByJobId не работает, с promises это очень просто: просто передайте callback как обратный вызов ошибки на Promise.all.

Ответ 2

JobRequest.findByJobId - асинхронная операция. Вы не можете блокировать асинхронные операции в JavaScript, поэтому вам нужно вручную синхронизировать, подсчитывая. Пример (обработка ошибок опущена для краткости):

var results = [];
var pendingJobCount = ids.length;

_.forEach(ids, function(id) {
    JobRequest.findByJobId(id, function(err, result) {
        results.push(result);
        if (--pendingJobCount === 0) callback(null, results);
    });
});

Есть, конечно, конструкторы обертки для создания подобных вещей, но я предпочитаю объяснять, как это работает. Зайдите в dfsq answer для получения более подробной информации об одной из этих оболочек, называемой promises.

Также обратите внимание, что асинхронные операции могут завершиться не в порядке. Порядок в массиве results не обязательно будет соответствовать порядку массива ids. Если вам нужна эта информация, вам нужно будет ее отслеживать самостоятельно, например, путем сбора результатов на карте вместо массива:

var results = {};
var pendingJobCount = ids.length;

_.forEach(ids, function(id) {
    JobRequest.findByJobId(id, function(err, result) {
        results[id] = result;
        if (--pendingJobCount === 0) callback(null, results);
    });
});

В этом примере предполагается, что в вашем массиве ids нет дубликатов. Результаты для дубликатов ключей будут отменены.

Обработка ошибок будет работать аналогичным образом, введя дополнительную информацию в ваш результат. Другой пример:

results.push({id: id, error: null, value: result});