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