Node.js:你如何处理循环中的callback?

我正在使用Node.js和Box SDK。 我的(失败!)代码如下所示:

var connection = box.getConnection(req.user.login); connection.ready(function () { connection.getFolderItems(0, null, function (err, result) { if (err) { opts.body = err; } else { opts.body = result; var a = []; for (var i=0; i < result.entries.length; i++) { connection.getFileInfo(result.entries[i].id, function (err, fileInfo) { if (err) { opts.body = err; } else { a.push(fileInfo); } });} } 

在“程序”方面,这是我想要做的:

 var connection= box.getConnection() var items = connection.getFolderItems() var itemList = new List foreach (item in items) { connection.getFileInfo(item.id) itemList.add(item) } display(itemList) 

我的问题是, connection.getFolderItems()connection.getFileInfo()是asynchronous的 – “for”循环在所有结果返回之前退出。

问:Node.js中的最佳方法是:1)获得第一个asynchronous调用的结果,2)遍历列表,进行更多的asynchronous调用,3)在“完成”时处理结果。

问: 承诺是一个很好的select吗?

问: 完成()/ next()是一个选项吗?

问:在这种场景下,Node.js中是否有“标准习惯用法”?

承诺是一个好主意,但你可能想看看asynchronous模块,特别是集合处理程序。 它允许您针对“事物”列表运行asynchronous调用,并在所有asynchronous调用完成时为您提供一个运行方法的地方。 不知道这是否比承诺好,但选项总是很好。

 // Should give you a rough idea async.each(items, function (item, callback) { connection.getFileInfo(result, callback); }, function (err) { console.log('All done'); }); 

https://github.com/caolan/async#each

问:Node.js中的最佳方法是:1)获得第一个asynchronous调用的结果,2)遍历列表,进行更多的asynchronous调用,3)在“完成”时处理结果。

您可以使用asynchronous库或promisify函数调用,并使用Promise来代替。 两者都很容易使用。

问:承诺是一个很好的select吗?

是。 但是它要求你在使用之前首先提出你的方法。

问:完成()/ next()是一个选项吗?

据我所知,这是一个完全不同的概念。 这里done是一个callback函数,你可以在方法结束后调用。 而next通常用于快速传递请求到下一个路由,我认为这与你所问的问题无关。

问:在这种场景下,Node.js中是否有“标准习惯用法”?

它通常被称为“asynchronous”或“非阻塞”呼叫

问:Node.js中的最佳方法是:1)获得第一个asynchronous调用的结果,2)遍历列表,进行更多的asynchronous调用,3)在“完成”时处理结果。

有多种方法。 手编码,承诺,asynchronous库。 “最佳”在旁观者的眼中,所以对我们来说这里不是真的。 我使用承诺所有我的asynchronous编码。 他们已经在ES6中正式标准化了,并且有很好的,健壮的实现(我喜欢Bluebird超越标准的简化复杂的asynchronous任务的额外function,它的promisifyAll()function为您提供任何标准asynchronous操作使用asynchronouscallback调用约定)。

我build议不要手动编码复杂的asynchronous操作,因为健壮的error handling非常困难,可以在asynchronouscallback中exception地使用exception,从而导致error handling和难以debugging的错误。 asynchronous库可能是最好的非承诺方式,因为它提供了一些基于asynchronouscallback的基础结构和同步function。

我个人更喜欢承诺。 我认为,随着时间的推移,我们会看到更多的asynchronousAPI标准化,以回报承诺,所以我认为这是一个前瞻性的学习和编程方式的更好select。

问:承诺是一个很好的select吗?

是的,承诺允许你运行一堆asynchronous操作,然后使用Promise.all()来知道它们什么时候全部完成。 它也会收集你所有asynchronous操作的结果。

问:完成()/ next()是一个选项吗?

我不完全确定你在这里问的是什么,但是你可以手动编写asynchronous操作来并行运行,并知道它们何时完成,或者依次运行它们,并知道它们何时完成。 承诺为你做了更多的这项工作,但是你可以在没有它们的情况下手动完成。

问:在这种场景下,Node.js中是否有“标准习惯用法”?

如果使用承诺,会有一个共同的方法来做到这一点。 如果不使用承诺,可能没有“标准习惯用语”,因为有很多不同的方法可以自己编写。

承诺实施

下面是一个在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 }); }); 

这是如何工作的:

  1. Promise的连接对象,以便其所有的方法有一个版本,返回一个承诺(只需在方法的末尾添加“asynchronous”来调用这个promisified版本)。

  2. 调用getFolderItemsAsync() ,其承诺将使用result.entries数组解决

  3. 运行该数组的映射,并行运行所有操作,并返回一个承诺,当所有操作完成后,用一个有序结果数组来parsing该承诺。

  4. 每个条目的实际结果都是通过connection.getFileInfoAsync()来实现的。

  5. 创buildparsing处理程序和拒绝处理程序。 如果在进程中的任何地方发生任何错误,它将传播到拒绝处理程序。 如果所有的操作都是成功的,最后的parsing处理程序将被调用一个有序的结果数组。

如果出现错误,上面的版本将中止,并且除错误代码外,您将得不到任何结果。 如果您想在出现错误时继续使用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)来检测你的asynchronous循环是否完成。 注意:这有不完整的error handling,这是手工编码的难点之一,而不是像承诺那样使用asynchronous框架。

 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 } }); } } }); });