Node.js循环asynchronous调用

我正忙于为报告系统的terminal工作。 节点async是给我的问题,虽然我宁愿不强迫它是同步的。

我们正在使用MongoDB和Mongoose。 我必须查询集合A的正则expression式,然后对于每个返回的文档,查询多个包含的文档来填充要返回的JSON对象/数组。

我可以使用populate大部分数据,除了最后的循环查询,这是asynchronous开始,并提前返回我的报告。 有没有一个优雅的方式来做到这一点? 或者,我应该分裂成一个不同的function,并多次呼吁坚持functions should do only one thing规则?

示例代码:

 A.find({ name: regex }).populate({ path: 'B', populate: { path: 'B.C', model: 'C' } }).exec(function(err, A) { var report = []; A.map(function(a)){ report[a.name] = []; D.aggregate([ { $match: { id: B._id } }, { $group: { _id: null, count: { $sum: 1 } } } ], function(err, result) { C.map(function(c){ report[a.name].push({ 'field1': c.field1, 'field2': c.field2, 'field3': c.field3, 'count': result.count }); }); }); } return report; }); 

这里的问题是逻辑/asynchronous。 不符合语法,因此是半伪代码。

任何帮助或build议将不胜感激。

你需要熟悉承诺,并与一般的asynchronous。 因为你正在返回一个数组,这就是你将得到的价值。

在处理Async时有几个选项,但在你的情况下,你想看两个解决scheme:

 // callbacks getSetOfIDs((err, ids) => { let remaining = ids.length; let things = []; let failed = false; ids.forEach(id => { getThingByID(id, (err, thing) => { if (failed) { return; } if (err) { failed = true; handleFailure(err); } else { remaining -= 1; things.push(thing); if (!remaining) { handleSuccess(things); } } }); }); }); 

请注意,我不会返回任何things ,而是将其传递给callback。

你可以使用高阶函数来清理这类东西。

 // cleaned up callbacks function handleNodeCallback (succeed, fail) { return function (err, data) { if (err) { fail(err); } else { succeed(data); } }; } function handleAggregateCallback (succeed, fail, count) { let items = []; let failed = false; const ifNotFailed = cb => data => { if (!failed) { cb(data); } }; const handleSuccess = ifNotFailed((item) => { items.push(item); if (items.length === count) { succeed(items); } }); const handleFailure = ifNotFailed((err) => { failed = true; fail(err); }); return handleNodeCallback(handleSuccess, handleFailure); } 

稍后有一个辅助代码,我们准备好了:

 // refactored callback app code (note that it's much less scary) getSetOfIDs((err, ids) => { const succeed = (things) => app.display(things); const fail = err => app.apologize(err); if (err) { return fail(err); } let onThingResponse = handleAggregateCallback(succeed, fail, ids.length); ids.forEach(id => getThingByID(id, onThingResponse)); }); 

请注意,除了高阶函数之外,我从来没有返回任何东西,我总是传递延续(下一步要做的事情)。

另一种方法是Promises

 // Promises getSetOfIDs() .then(ids => Promise.all(ids.map(getThingByID))) .then(things => app.display(things)) .catch(err => app.apologize(err)); 

要真正了解这里发生了什么,学习Promise,Promise.all静态方法和array.map()

这两套代码在理论上都做了完全相同的事情,除了在最后一种情况下, getSetOfIDsgetThingByID不采用callback,而是返回promise。

通常在asynchronous调用中,在return语句之后,任何操作都被取消。

也许你只有完成了所有的工作才能返回报表对象。

 A.find({ name: regex }).populate({ path: 'B', populate: { path: 'B.C', model: 'C' } }).exec(function(err, A) { var report = []; A.map(function(a)){ report[a.name] = D.aggregate([ { $match: { id: B._id } }, { $group: { _id: null, count: { $sum: 1 } } } ], function(err, result) { if(err){ return []; } var fields = [] C.map(function(c){ fields.push({ 'field1': c.field1, 'field2': c.field2, 'field3': c.field3, 'count': result.count }); }); return fields; }); } return report; }); 

只要使用承诺:

 A.find({ name: regex }).populate({ path: 'B', populate: { path: 'B.C', model: 'C' } }).exec(function(err, A) { var report = []; return Promise.all([ A.map(function(a)){ return new Promise(function(resolve, reject) { report[a.name] = []; D.aggregate([{ $match: { id: B._id }},{$group: {_id: null,count: { $sum: 1 }}}], function(err, result) { if(err) { reject(err) } else { C.map(function(c){ report[a.name].push({ 'field1': c.field1, 'field2': c.field2, 'field3': c.field3, 'count': result.count }); }); resolve(report) } }); } })]) }) .then(function(report){ console.log(report) }) .catch(function(err){ console.log(err) })