我正在尝试将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和回调将在调用内部函数之前运行。我该如何解决这个问题?
我想在for和inner函数完成之后调用回调。
答案 0 :(得分:12)
另一种方法是将所有内容包装到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
请求失败时,您还需要处理潜在的情况,承诺非常简单:只需将callback
作为错误回调传递给Promise.all
。
答案 1 :(得分:8)
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's 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});