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

I am using Node.js and Box SDK. My code (crash!) Is as follows:

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 what I am 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 ends before all results are returned.

Q: What is the best way in Node.js to 1) get the result of the first asynchronous call, 2) iterate through the list, make more asynchronous calls, and 3) process the results when everything is done.

Q: Are there promises a good choice here?

Q: done () / next () option?

Q: Is there a “standard idiom” in Node.js for this kind of scenario?

+7
asynchronous for-loop
source share
3 answers

Promises is a great idea, but you might want to take a look at the asynchronous module, in particular the collection handlers. It allows you to run asynchronous calls with a list of "things" and gives you a place to run the method when all asynchronous calls are complete. I don't know if this is better than promises, but the 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

+9
source share

Q: What is the best way in Node.js to 1) get the result of the first asynchronous call, 2) iterate over the list, make asynchronous calls, and 3) process the results when everything is "done".

There are several approaches. Manual coding, Promises, Asynchronous library. “The best” is in the eye of the beholder, so we do not need to speak here. I use Promises for all of my asynchronous coding. They are officially standardized in ES6, and there are good, reliable implementations (I like Bluebird for additional functions, it surpasses the standard that simplifies complex async tasks and for it the promisifyAll() function that gives you the promise interface for any standard async operation that uses asynchronous call invocation agreement).

I would advise you not to perform complex asynchronous operations with manual coding, because reliable error handling is very complex, and exceptions can be used silently inside asynchronous callbacks, which leads to loss of error handling and difficult debugging. The Async library is probably the best way to incompatibility, as it provides some infrastructure and synchronization features in asynchronous callbacks.

I personally prefer promises. I think we will see that the async API standards are standardized, returning a promise as we move forward, so I think this is the best choice for a promising way to learn and program.

Q: Are Promises a good choice?

Yes, Promises allows you to run a bunch of asynchronous operations, and then use something like Promise.all() to know when everything will be done. It will also collect all the results of all asynchronous operations for you.

Q: Completed () / next () parameter?

I'm not quite sure what you are asking here, but you can manually perform async operations for parallel operation, and also know when this is done, or run them sequentially and know when they will be executed. Promises do a lot more for you, but you can do it manually without them.

Q: Is there a “standard idiom” in Node.js for this kind of scenario?

If you use Promises, there will be a general way to do this. If you don't use Promises, there probably isn't a “standard idiom,” since there are many different ways to encode it.

Promise Implementation

Here is an example using the Promise Bluebird library in Node.js:

 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 it works:

  • Advance the connection object so that all its methods have a version that returns a promise (just add “Async” to the end of the method to call this promising version).

  • A call to getFolderItemsAsync() , and its promise will be resolved using the result.entries array

  • Run the map of this array by doing all the parallel operations and returning the promise that is resolved using an array of ordered results when all operations are performed.

  • The actual result for each record is achieved using connection.getFileInfoAsync() .

  • Create a permission handler and a reject handler. If any error occurs anywhere in the process, it will propagate to the deviation handler. If all operations are successful, the last handler will be called with an ordered array of results.

In the above version, it is interrupted if there is an error, and you do not get any results, except for the error code. If you want to continue the result null , if there is an error, you can use something like this:

 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 entered version

It uses a manually encoded version. The key to this is detecting when your asynchronous loop is executed by comparing if (results.length === result.entries.length) . Note. This is an incomplete error handling, which is one of the difficulties with coding this manually, and not using the async framework like 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 } }); } } }); }); 
+5
source share

Q: What is the best way in Node.js to 1) get the result of the first asynchronous call, 2) iterate over the list, make asynchronous calls, and 3) process the results when everything is "done".

You can either use the async library, or disfigure function calls and use Promise. Both are easy to use.

Q: Are promises a good choice here?

Yes. But this requires that you first promise your method before use.

Q: Completed () / next () parameter?

From what I understand, this is a completely different concept. done here refers to a callback function that you can call after the method completes. And next usually used in express to send a request to the next route, which, I think, is not related to the question that you are asking.

Q: Is there a “standard idiom” in Node.js for this kind of scenario?

It is usually referred to only as “asynchronous” or “non-blocking” calls.

+4
source share

All Articles