On the basis of the question's title, "Resolve promises one after another (i.e. in sequence)?", we might understand that the OP is more interested in the sequential handling of promises on settlement than sequential calls per se.
This answer is offered :
- to demonstrate that sequential calls are not necessary for sequential handling of responses.
- to expose viable alternative patterns to this page's visitors - including the OP if he is still interested over a year later.
- despite the OP's assertion that he does not want to make calls concurrently, which may genuinely be the case but equally may be an assumption based on the desire for sequential handling of responses as the title implies.
If concurrent calls are genuinely not wanted then see Benjamin Gruenbaum's answer which covers sequential calls (etc) comprehensively.
If however, you are interested (for improved performance) in patterns which allow concurrent calls followed by sequential handling of responses, then please read on.
It's tempting to think you have to use Promise.all(arr.map(fn)).then(fn)
(as I have done many times) or a Promise lib's fancy sugar (notably Bluebird's), however (with credit to this article) an arr.map(fn).reduce(fn)
pattern will do the job, with the advantages that it :
- works with any promise lib - even pre-compliant versions of jQuery - only
.then()
is used.
- affords the flexibility to skip-over-error or stop-on-error, whichever you want with a one line mod.
Here it is, written for Q
.
var readFiles = function(files) {
return files.map(readFile) //Make calls in parallel.
.reduce(function(sequence, filePromise) {
return sequence.then(function() {
return filePromise;
}).then(function(file) {
//Do stuff with file ... in the correct sequence!
}, function(error) {
console.log(error); //optional
return sequence;//skip-over-error. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+).
});
}, Q()).then(function() {
// all done.
});
};
Note: only that one fragment, Q()
, is specific to Q. For jQuery you need to ensure that readFile() returns a jQuery promise. With A+ libs, foreign promises will be assimilated.
The key here is the reduction's sequence
promise, which sequences the handling of the readFile
promises but not their creation.
And once you have absorbed that, it's maybe slightly mind-blowing when you realise that the .map()
stage isn't actually necessary! The whole job, parallel calls plus serial handling in the correct order, can be achieved with reduce()
alone, plus the added advantage of further flexibility to :
- convert from parallel async calls to serial async calls by simply moving one line - potentially useful during development.
Here it is, for Q
again.
var readFiles = function(files) {
return files.reduce(function(sequence, f) {
var filePromise = readFile(f);//Make calls in parallel. To call sequentially, move this line down one.
return sequence.then(function() {
return filePromise;
}).then(function(file) {
//Do stuff with file ... in the correct sequence!
}, function(error) {
console.log(error); //optional
return sequence;//Skip over any errors. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+).
});
}, Q()).then(function() {
// all done.
});
};
That's the basic pattern. If you wanted also to deliver data (eg the files or some transform of them) to the caller, you would need a mild variant.
readFileSequential()
has already returned before the next one is called (because it's async, it completes long after the original function call has already returned). – Epergne