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 будут работать до того, как будет вызвана внутренняя функция. Как я могу это решить?
Я хочу, чтобы вызываемый вызов вызывался после завершения каждой и внутренней функции.
Ответы
Ответ 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});