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

时间:2016-04-27 02:06:43

标签: node.js for-loop asynchronous

我正在使用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()是异步的 - 在返回所有结果之前退出“for”循环。

问:Node.js中最好的方法是1)获得第一个异步调用的结果,2)遍历列表,进行更多异步调用,以及3)当所有内容都“完成”时处理结果

问:这里promises是个不错的选择吗?

问:done()/next()可以选择吗?

问:对于这种情况,Node.js中是否有“标准习惯用法”?

3 个答案:

答案 0 :(得分:11)

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

// 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

答案 1 :(得分:5)

  

问:Node.js中最好的方法是1)得到第一个结果   异步调用,2)遍历列表,进行更多异步调用,以及   3)当一切都完成后,处理结果&#34;。

有多种方法。手动编码,Promises,异步库。 &#34;最佳&#34;是在旁观者的眼中,所以我们不能在这里说。我使用Promises进行所有异步编码。它们已经在ES6中正式标准化,并且有很好的,强大的实现(我喜欢Bluebird的超出标准的额外功能,可以简化复杂的异步任务以及它的promisifyAll()功能,它可以为您提供承诺任何使用异步回调调用约定的标准异步操作的接口。

我建议不要手工编码复杂的异步操作,因为强大的错误处理非常困难,并且可以在异步回调中静默处理异常,从而导致错误处理丢失和调试困难。 Async库可能是最好的非Promise方式,因为它提供了围绕异步回调的一些基础架构和同步功能。

我个人更喜欢承诺。我认为随着时间的推移,我们会看到更多异步API标准化返回承诺,因此我认为这是一种更好的选择,可以用于前瞻性的学习和编程方式。

  

问:承诺在这里是一个不错的选择吗?

是的,promises允许你运行一堆异步操作,然后使用Promise.all()之类的东西知道它们何时完成。它还将收集所有异步操作的所有结果。

  

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

我不确定你在这里问的是什么,但是你可以手动编写异步操作代码并行运行并知道它们何时完成或按顺序运行它们并知道它们何时完成。 Promise为你做了很多这项工作,但你可以在没有它们的情况下手动完成。

  问:有没有&#34;标准成语&#34;在Node.js中针对这种情况?

如果使用promises,会有一种常见的方法来执行此操作。如果不使用承诺,可能没有&#34;标准成语&#34;因为有很多不同的方法可以自己编码。

承诺实施

以下是使用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. Promisify连接对象,以便其所有方法都有一个返回promise的版本(只需在方法的末尾添加&#34; Async&#34;以调用此promisified版本)。

  2. 致电getFolderItemsAsync(),其承诺将通过result.entries数组解析

  3. 运行该数组的映射,并行运行所有操作并返回一个promise,当所有操作完成后,该promise将使用一系列有序结果解析。

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

  5. 创建解析处理程序和拒绝处理程序。如果进程中的任何位置发生任何错误,它将传播到拒绝处理程序。如果所有操作都成功,将使用有序的结果数组调用最后一个解析处理程序。

  6. 如果出现错误,则上述版本将中止,除了错误代码之外,您没有得到任何结果。如果您想继续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)来检测异步循环何时完成。注意:这有不完整的错误处理,这是手动编码和不使用像promises这样的异步框架的困难之一。

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

答案 2 :(得分:4)

  

问:Node.js中最好的方法是1)得到第一个结果   异步调用,2)遍历列表,进行更多异步调用,以及   3)当一切都“完成”时处理结果。

您可以使用async库或宣传函数调用并改为使用Promise。两者都很容易使用。

  

问:承诺在这里是一个不错的选择吗?

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

  

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

据我所知,这是一个完全不同的概念。这里的done指的是一个回调函数,你可以在方法完成后调用它。并且next通常用于快递以传递对下一条路线的请求,我认为这与您提出的问题无关。

  

问:对于这种情况,Node.js中是否有“标准习惯用法”?

它通常被称为“异步”或“非阻塞”调用