【问题标题】:Node.js: How do you handle callbacks in a loop?Node.js:如何处理循环中的回调?
【发布时间】:2016-08-21 02:44:16
【问题描述】:

我正在使用 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 中是否有针对这种场景的“标准习语”?

【问题讨论】:

    标签: node.js for-loop asynchronous


    【解决方案1】:

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

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

    【讨论】:

    • 我收到的所有回复都很棒 - 谢谢!就像你和 JasonWihardja 建议的那样,我最终使用了异步。这是一个很好的教程:justinklemm.com/node-js-async-tutorial
    • 我仍然认为 promises 最终是这样做的更惯用的方式,但我很高兴你发现它很有用。
    【解决方案2】:

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

    这是如何工作的:

    1. Promisify 连接对象,使其所有方法都有一个返回 Promise 的版本(只需在方法末尾添加“Async”即可调用此 Promisified 版本)。

    2. 调用getFolderItemsAsync(),它的promise将通过result.entries数组解决

    3. 运行该数组的映射,并行运行所有操作,并返回一个 Promise,当所有操作完成后,该 Promise 会以有序结果数组进行解析。

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

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

    如果出现错误并且除了错误代码之外您没有得到任何结果,上述版本将中止。如果您想在出现错误的情况下继续使用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
                        }
                    });
                }
            }
        });
    });
    

    【讨论】:

      【解决方案3】:

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

      您可以使用async 库或promisify 函数调用并改用Promise。两者都易于使用。

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

      是的。但它要求你在使用前先承诺你的方法。

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

      据我了解,这是一个完全不同的概念。这里的done 指的是一个回调函数,你可以在方法完成后调用它。而next 通常用于 express 将请求传递到下一个路由,我认为这与您提出的问题无关。

      问:Node.js 中是否有针对这种场景的“标准习语”?

      通常只指“异步”或“非阻塞”调用

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-07
        • 2015-08-10
        • 1970-01-01
        相关资源
        最近更新 更多