28

I'm trying to get my head around promises, using the Q module in node.js, however I have a small issue.

In this example:

ModelA.create(/* params */)
.then(function(modelA){
    return ModelB.create(/* params */);
})
.then(function(modelB){
    return ModelC.create(/* params */);
})
.then(function(modelC){

    // need to do stuff with modelA, modelB and modelC

})
.fail(/*do failure stuff*/);

The .create method returns a promise then in each .then(), as expected one gets the resolved value of the promise.

However in the final .then() I need to have all 3 previously resolved promise values.

What would be the best way to do this?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
MoSs
  • 565
  • 1
  • 4
  • 11
  • exact duplicate of [How do I access previous promise results in a .then() chain?](http://stackoverflow.com/q/28250680/1048572) – Bergi Jan 31 '15 at 11:02

3 Answers3

35

These are some of your many options:

Behind door 1, use reduce to accumulate the results in series.

var models = [];
[
    function () {
        return ModelA.create(/*...*/);
    },
    function () {
        return ModelB.create(/*...*/);
    },
    function () {
        return ModelC.create(/*...*/);
    }
].reduce(function (ready, makeModel) {
    return ready.then(function () {
        return makeModel().then(function (model) {
            models.push(model);
        });
    });
}, Q())
.catch(function (error) {
    // handle errors
});

Behind door 2, pack the accumulated models into an array, and unpack with spread.

Q.try(function () {
    return ModelA.create(/* params */)
})
.then(function(modelA){
    return [modelA, ModelB.create(/* params */)];
})
.spread(function(modelA, modelB){
    return [modelA, modelB, ModelC.create(/* params */)];
})
.spread(function(modelA, modelB, modelC){
    // need to do stuff with modelA, modelB and modelC
})
.catch(/*do failure stuff*/);

Behind door 3, capture the results in the parent scope:

var models [];
ModelA.create(/* params */)
.then(function(modelA){
    models.push(modelA);
    return ModelB.create(/* params */);
})
.then(function(modelB){
    models.push(modelB);
    return ModelC.create(/* params */);
})
.then(function(modelC){
    models.push(modelC);

    // need to do stuff with models

})
.catch(function (error) {
    // handle error
});
Kris Kowal
  • 3,866
  • 2
  • 24
  • 24
  • Before I posted this question, I already had done it exactly like "door #3" :) However I wasn't too comfortable with this approach and wanted to know other ways of doing this. So I will pick door #2 as it doesn't require the "help" of an "outside" variable. BTW I have to say Kris, awesome work with Q there, may callbacks die forever :) – MoSs Sep 18 '13 at 19:12
  • I like door #1 for its cleverness, but it's totally unreadable at first glance. It takes some brain power to grok it whereas door number 2 is immediately readable. Thanks for this answer :) – CatDadCode May 15 '15 at 01:29
23

The Bluebird promises library provides another solution for this via .bind().

It looks like this:

ModelA.create(/* params */).bind({})
.then(function (modelA) {
    this.modelA = modelA;
    return ModelB.create(/* params */);
})
.then(function (modelB) {
    this.modelB = modelB;
    return ModelC.create(/* params */);
})
.then(function (modelC) {
    // you have access to this.modelA, this.modelB and modelC;
});

There is a lot of interesting information about this method in the documentation.

justmoon
  • 1,311
  • 1
  • 10
  • 9
3

You probably doesn't need to wait until modelA is created to create modelB and so on.
If this is true, then you can do the following:

var promises = [
  ModelA.create(...),
  ModelB.create(...),
  ModelC.create(...)
);

Q.all( promises ).spread(function( modelA, modelB, modelC ) {
  // Do things with them!
}).fail(function() {
  // Oh noes :(
});

What this does is:

  • Create an array of promises, with one promise for each model you need;
  • Execute all 3 promises in parallel;
  • Execute a function passed in spread() when all 3 promises are done. The arguments are the resolved values for each promise, in the declaration order.

I hope it helps you :)

gustavohenke
  • 40,997
  • 14
  • 121
  • 129
  • Thank you, this worked in my particular situation, however still I'd like to know a way to have those promises executed in order and each one wait from the previous to become fulfilled and in the end get the results from all. – MoSs Sep 17 '13 at 13:03
  • One way to do this is by setting variables in an accessible scope (the one which `ModelA.create()` was called, for example). – gustavohenke Sep 17 '13 at 14:01