简体   繁体   English

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

[英]Node.js: How do you handle callbacks in a loop?

I'm using Node.js and Box SDK. 我正在使用Node.js和Box SDK。 My (failing!) code looks like this: 我的(失败!)代码如下所示:

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

In "procedural" terms, this is which I'm trying to do: 在“程序”术语中,这是我想要做的:

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)

My problem is that connection.getFolderItems() and connection.getFileInfo() are asynchronous - the "for" loop exits before all the results are returned. 我的问题是connection.getFolderItems()connection.getFileInfo()是异步的 - 在返回所有结果之前,“for”循环退出。

Q: What is the best way in Node.js to 1) get the result of the first async call, 2) iterate through the list, making more async calls, and 3) process the results when everything is "done". 问:Node.js中最好的方法是1)获得第一个异步调用的结果,2)遍历列表,进行更多的异步调用,以及3)当所有内容都“完成”时处理结果。

Q: Are promises a good choice here? 问: 承诺在这里是一个不错的选择吗?

Q: Is done()/next() an option? 问: 完成()/ next()是一个选项吗?

Q: Is there any "standard idiom" in Node.js for this kind of scenario? 问:对于这种情况,Node.js中是否有任何“标准习惯用法”?

Promises are a great idea, but you may want to take a look at the async module, specifically the collection handlers. Promise是一个好主意,但你可能想看看async模块,特别是集合处理程序。 It allows you to run async calls against a list of 'things' and gives you a place to run a method when all of the async calls are done. 它允许您针对“事物”列表运行异步调用,并在完成所有异步调用时为您提供运行方法的位置。 Don't know if this is better than promises, but options are always nice. 不知道这是否比承诺更好 ,但选择总是很好。

// 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 https://github.com/caolan/async#each

Q: What is the best way in Node.js to 1) get the result of the first async call, 2) iterate through the list, making more async calls, and 3) process the results when everything is "done". 问:Node.js中最好的方法是1)获得第一个异步调用的结果,2)遍历列表,进行更多的异步调用,以及3)当所有内容都“完成”时处理结果。

There are multiple approaches. 有多种方法。 Hand coding, Promises, Async library. 手动编码,Promises,异步库。 "Best" is in the eye of the beholder so not really for us to say here. “最好的”是旁观者的眼睛,所以我们不能在这里说。 I use Promises for all my async coding. 我使用Promises进行所有异步编码。 They have been officially standardized in ES6 and there are good, robust implementations (I like Bluebird for the extra features it has beyond the standard that simplify complex async tasks and for it's promisifyAll() feature which gives you a promise interface on any standard async operation that uses the async callback calling convention). 它们已在ES6中正式标准化,并且有良好,强大的实现(我喜欢Bluebird的超出标准的额外功能,简化了复杂的异步任务,以及它的promisifyAll()功能,它为任何标准的异步操作提供了一个promise接口使用异步回调调用约定)。

I'd advise against hand coding complicated async operations because robust error handling is very difficult and exceptions can be silently eaten inside of async callbacks leading to lost error handling and difficult debugging. 我建议不要手工编写复杂的异步操作,因为强大的错误处理非常困难,并且可以在异步回调中静默地使用异常,从而导致错误处理丢失和调试困难。 The Async library is probably the best non-Promise way of doing things as it provides some infrastructure and synchronization features around async callbacks. Async库可能是最好的非Promise方式,因为它提供了围绕异步回调的一些基础架构和同步功能。

I personally prefer promises. 我个人更喜欢承诺。 I think we'll see more async APIs standardizing on returning a promise as time marches forward so I think it's a better choice for a forward-looking way to learn and program. 我认为随着时间的推移,我们会看到更多异步API标准化返回承诺,因此我认为这是一种更好的选择,可以用于前瞻性的学习和编程方式。

Q: Are promises a good choice here? 问:承诺在这里是一个不错的选择吗?

Yes, promises allow you to run a bunch of asynchronous operations and then use something like Promise.all() to know when they are all done. 是的,promises允许你运行一堆异步操作,然后使用像Promise.all()这样的东西来知道它们什么时候完成。 It will also collect all the results from all the async operations for you. 它还将为您收集所有异步操作的所有结果。

Q: Is done()/next() an option? 问:完成()/ next()是一个选项吗?

I'm not exactly sure what you are asking about here, but you can manually code async operations to either run in parallel and know when they are done or to run them sequentially and know when they are done. 我不确定你在这里问的是什么,但你可以手动编写异步操作代码并行运行并知道它们何时完成或按顺序运行它们并知道它们何时完成。 Promises do a lot more of this work for you, but you can do it manually without them. Promise为你做了很多这项工作,但你可以不用它们手动完成。

Q: Is there any "standard idiom" in Node.js for this kind of scenario? 问:对于这种情况,Node.js中是否有任何“标准习惯用法”?

If using promises, there would be a common way to do this. 如果使用promises,会有一种常见的方法来执行此操作。 If not using promises, there is probably not a "standard idiom" as there are many different ways to code it yourself. 如果不使用promises,可能没有“标准习语”,因为有很多不同的方法可以自己编写代码。

Promise Implementation 承诺实施

Here's an example using the Bluebird Promise library in 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
    });
});

Here's how this works: 这是如何工作的:

  1. Promisify the connection object so that all its methods have a version that returns a promise (just add "Async" onto the end of the method to call this promisified version). Promisify连接对象,以便其所有方法都有一个返回promise的版本(只需在方法的末尾添加“Async”以调用此promisified版本)。

  2. Call getFolderItemsAsync() and its promise will resolve with the result.entries array 调用getFolderItemsAsync()并使用result.entries数组解析其promise

  3. Run a map of that array, running all the operations in parallel and returning a promise that resolves with an array of ordered results when all the operations are done. 运行该数组的映射,并行运行所有操作并返回一个promise,当完成所有操作后,该promise将使用一系列有序结果进行解析。

  4. The actual result for each entry is achieved with connection.getFileInfoAsync() . 每个条目的实际结果都是通过connection.getFileInfoAsync()实现的。

  5. Create a resolve handler and a reject handler. 创建解析处理程序和拒绝处理程序。 If any error occurs anywhere in the process, it will propagate up to the reject handler. 如果进程中的任何位置发生任何错误,它将传播到拒绝处理程序。 If all operations are successful, the last resolve handler will be called with an ordered array of results. 如果所有操作都成功,将使用有序的结果数组调用最后一个解析处理程序。

The version above aborts if there's an error and you get no results other than the error code. 如果出现错误,则上述版本将中止,除了错误代码之外,您没有得到任何结果。 If you want to continue with a null result if there's an error, then you can use something like this: 如果您想在出现错误时继续使用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
    });
});

Manually Coded Version 手动编码版本

Here's a manually coded version. 这是一个手动编码的版本。 The key to this one is detecting when your async loop is done by comparing if (results.length === result.entries.length) . 这个的关键是通过比较if (results.length === result.entries.length)来检测异步循环何时完成。 Note: This has incomplete error handling which is one of the difficulties of coding this by hand and not using an async framework like promises. 注意:这有不完整的错误处理,这是手动编码和不使用像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
                    }
                });
            }
        }
    });
});

Q: What is the best way in Node.js to 1) get the result of the first async call, 2) iterate through the list, making more async calls, and 3) process the results when everything is "done". 问:Node.js中最好的方法是1)获得第一个异步调用的结果,2)遍历列表,进行更多的异步调用,以及3)当所有内容都“完成”时处理结果。

You could either use async library or promisify the function calls and use Promise instead. 您既可以使用异步库,也可以使用promise来调用函数调用,而是使用Promise。 Both are easy to use. 两者都很容易使用。

Q: Are promises a good choice here? 问:承诺在这里是一个不错的选择吗?

Yes. 是。 But it requires you to promisify your method first before using. 但它要求您在使用之前首先宣传您的方法。

Q: Is done()/next() an option? 问:完成()/ next()是一个选项吗?

From what I understand, it's an entirely different concept. 据我所知,这是一个完全不同的概念。 The done here refers to a callback function which you can call after the method finishes. 这里done是指一个回调函数,您可以在方法完成后调用它。 And next is usually used in express to pass on a request to the next route, which I think is not relevant to the question you're asking. next通常用于快递以传递对下一条路线的请求,我认为这与您提出的问题无关。

Q: Is there any "standard idiom" in Node.js for this kind of scenario? 问:对于这种情况,Node.js中是否有任何“标准习惯用法”?

It's usually referred to just "async" or "non blocking" calls 它通常被称为“异步”或“非阻塞”调用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM