A second attempt at an answer in which I try to be more explanatory:
First, some requisite background, from the RSVP README:
The really awesome part comes when you return a promise from the first handler...This allows you to flatten out nested callbacks, and is the main feature of promises that prevents "rightward drift" in programs with a lot of asynchronous code.
This is precisely how you make promises sequential, by returning the later promise from the then
of the promise that should finish before it.
It is helpful to think of such a set of promises as a tree, where the branches represent sequential processes, and the leaves represent concurrent processes.
The process of building up such a tree of promises is analogous to the very common task of building other sorts of trees: maintain a pointer or reference to where in the tree you are currently adding branches, and iteratively add things.
As @Esailija pointed out in his answer, if you have an array of promise-returning functions that don't take arguments you can use reduce
to neatly build the tree for you. If you've ever implemented reduce for yourself, you will understand that what reduce is doing behind the scenes in @Esailija's answer is maintaining a reference to the current promise (cur
) and having each promise return the next promise in its then
.
If you DON'T have a nice array of homogeneous (with respect to the arguments they take/return) promise returning functions, or if you need a more complicated structure than a simple linear sequence, you can construct the tree of promises yourself by maintaining a reference to the position in the promise tree where you want to add new promises:
var root_promise = current_promise = Ember.Deferred.create();
// you can also just use your first real promise as the root; the advantage of
// using an empty one is in the case where the process of BUILDING your tree of
// promises is also asynchronous and you need to make sure it is built first
// before starting it
current_promise = current_promise.then(function(){
return // ...something that returns a promise...;
});
current_promise = current_promise.then(function(){
return // ...something that returns a promise...;
});
// etc.
root_promise.resolve();
You can build combinations of concurrent and sequential processes by using RSVP.all to add multiple "leaves" to a promise "branch". My downvoted-for-being-too-complicated answer shows an example of that.
You can also use Ember.run.scheduleOnce('afterRender') to ensure that something done in one promise gets rendered before the next promise is fired -- my downvoted-for-being-too-complicated answer also shows an example of that.