问:Node.js 中最好的方法是 1) 得到第一个的结果
异步调用,2)遍历列表,进行更多异步调用,以及
3) 当一切都“完成”时处理结果。
有多种方法。手工编码,Promises,异步库。 “最好”在旁观者的眼中,所以我们不能在这里说。我使用 Promises 进行所有异步编码。它们已在 ES6 中正式标准化,并且有良好、健壮的实现(我喜欢 Bluebird,因为它具有超出标准的额外功能,可以简化复杂的异步任务,并且它的 promisifyAll() 功能为您提供了任何标准异步操作的承诺接口使用异步回调调用约定)。
我建议不要手动编写复杂的异步操作,因为健壮的错误处理非常困难,并且异常可以在异步回调中静默吃掉,从而导致错误处理丢失和调试困难。 Async 库可能是最好的非 Promise 做事方式,因为它围绕异步回调提供了一些基础设施和同步功能。
我个人更喜欢承诺。我认为随着时间的推移,我们会看到更多的异步 API 标准化返回一个 Promise,因此我认为这是一种具有前瞻性的学习和编程方式的更好选择。
问:promise 在这里是一个不错的选择吗?
是的,promises 允许您运行一堆异步操作,然后使用 Promise.all() 之类的东西来知道它们什么时候完成。它还会为您收集所有异步操作的所有结果。
问:done()/next() 是一个选项吗?
我不确定您在此处询问的内容,但您可以手动编写异步操作代码以并行运行并知道它们何时完成,或者按顺序运行它们并知道它们何时完成。 Promise 为您完成了更多此类工作,但您可以在没有它们的情况下手动完成。
问:Node.js 中是否有针对这种场景的“标准习语”?
如果使用 Promise,会有一个通用的方法来做到这一点。如果不使用 Promise,可能没有“标准习语”,因为有很多不同的方式可以自己编写代码。
承诺实施
这是在 node.js 中使用 Bluebird Promise 库的示例:
var Promise = require('bluebird');
var connection = Promise.promisifyAll(box.getConnection(req.user.login));
connection.ready(function() {
connection.getFolderItemsAsync(0, null).then(function(result) {
return Promise.map(result.entries, function(item) {
return connection.getFileInfoAsync(item.id);
})
}).then(function(results) {
// array of results here
}, function(err) {
// error here
});
});
这是如何工作的:
Promisify 连接对象,使其所有方法都有一个返回 Promise 的版本(只需在方法末尾添加“Async”即可调用此 Promisified 版本)。
调用getFolderItemsAsync(),它的promise将通过result.entries数组解决
运行该数组的映射,并行运行所有操作,并返回一个 Promise,当所有操作完成后,该 Promise 会以有序结果数组进行解析。
每个条目的实际结果是用connection.getFileInfoAsync()实现的。
创建解析处理程序和拒绝处理程序。如果过程中的任何地方发生任何错误,它将传播到拒绝处理程序。如果所有操作都成功,最后一个解析处理程序将被调用,并带有一个有序的结果数组。
如果出现错误并且除了错误代码之外您没有得到任何结果,上述版本将中止。如果您想在出现错误的情况下继续使用null 结果,那么您可以使用以下内容:
var Promise = require('bluebird');
var connection = Promise.promisifyAll(box.getConnection(req.user.login));
connection.ready(function() {
connection.getFolderItemsAsync(0, null).then(function(result) {
return Promise.map(result.entries, function(item) {
return connection.getFileInfoAsync(item.id).catch(function(err){
// post the results as null and continue with the other results
return null;
});
})
}).then(function(results) {
// array of results here (some might be null if they had an error)
}, function(err) {
// error here
});
});
手动编码版本
这是一个手动编码的版本。这个的关键是通过比较if (results.length === result.entries.length) 来检测你的异步循环何时完成。注意:这具有不完整的错误处理,这是手动编码而不使用像 Promise 这样的异步框架的困难之一。
var connection = box.getConnection(req.user.login);
connection.ready(function () {
connection.getFolderItems(0, null, function (err, result) {
if (err) {
// do error handling here
opts.body = err;
} else {
var results = [];
for (var i = 0; i < result.entries.length; i++) {
connection.getFileInfo(result.entries[i].id, function (err, fileInfo) {
if (err) {
// do error handling here
opts.body = err;
results.push(null);
} else {
results.push(fileInfo);
}
// if done with all requests
if (results.length === result.entries.length) {
// done with everything, results are in results
// process final results here
}
});
}
}
});
});