How do I access previous promise results in a .then() chain?
Asked Answered
I

17

754

I have restructured my code to promises, and built a wonderful long flat promise chain, consisting of multiple .then() callbacks. In the end I want to return some composite value, and need to access multiple intermediate promise results. However the resolution values from the middle of the sequence are not in scope in the last callback, how do I access them?

function getExample() {
    return promiseA(…).then(function(resultA) {
        // Some processing
        return promiseB(…);
    }).then(function(resultB) {
        // More processing
        return // How do I gain access to resultA here?
    });
}
Intercellular answered 31/1, 2015 at 10:41 Comment(1)
This question is really interesting and even if it is tagged javascript, it is relevant in other language. I just use the "break the chain" answer in java and jdeferredBaum
I
269

ECMAScript Harmony

Of course, this problem was recognized by the language designers as well. They did a lot of work and the async functions proposal finally made it into

ECMAScript 8

You don't need a single then invocation or callback function anymore, as in an asynchronous function (that returns a promise when being called) you can simply wait for promises to resolve directly. It also features arbitrary control structures like conditions, loops and try-catch-clauses, but for the sake of convenience we don't need them here:

async function getExample() {
    var resultA = await promiseA(…);
    // some processing
    var resultB = await promiseB(…);
    // more processing
    return // something using both resultA and resultB
}

ECMAScript 6

While we were waiting for ES8, we already did use a very similar kind of syntax. ES6 came with generator functions, which allow breaking the execution apart in pieces at arbitrarily placed yield keywords. Those slices can be run after each other, independently, even asynchronously - and that's just what we do when we want to wait for a promise resolution before running the next step.

There are dedicated libraries (like co or task.js), but also many promise libraries have helper functions (Q, Bluebird, when, …) that do this async step-by-step execution for you when you give them a generator function that yields promises.

var getExample = Promise.coroutine(function* () {
//               ^^^^^^^^^^^^^^^^^ Bluebird syntax
    var resultA = yield promiseA(…);
    // some processing
    var resultB = yield promiseB(…);
    // more processing
    return // something using both resultA and resultB
});

This did work in Node.js since version 4.0, also a few browsers (or their dev editions) did support generator syntax relatively early.

ECMAScript 5

However, if you want/need to be backward-compatible you cannot use those without a transpiler. Both generator functions and async functions are supported by the current tooling, see for example the documentation of Babel on generators and async functions.

And then, there are also many other compile-to-JS languages that are dedicated to easing asynchronous programming. They usually use a syntax similar to await, (e.g. Iced CoffeeScript), but there are also others that feature a Haskell-like do-notation (e.g. LatteJs, monadic, PureScript or LispyScript).

Intercellular answered 31/1, 2015 at 10:43 Comment(7)
@Intercellular do you need to await the async function examle getExample() from outside code?Hotbox
@arisalexis: Yes, getExample is still a function that returns a promise, working just like the functions in the other answers, but with nicer syntax. You could await a call in another async function, or you could chain .then() to its result.Intercellular
I'm curious, why did you answer your own question immediately after asking it? There is some good discussion here, but I'm curious. Maybe you found your answers on your own after asking?Helve
@granmoe: I posted the whole discussion on purpose as a canonical duplicate targetIntercellular
Is there a (not too laborious) way to avoid using Promise.coroutine (i.e., not using Bluebird or another library, but only plain JS) in the ECMAScript 6 example with the generator function? I had in mind something like steps.next().value.then(steps.next)... but that didn't work.Vilhelmina
@DanielMH You can do it manually without any helper function, but that is always laborious and error-prone. I can absolutely not recommend it. But unless you cannot use a transpiler, you'd be using async/await anyway.Intercellular
@Intercellular Thanks for the quick answer! I had hoped there would be a 'simple' solution (like the one I indicated: stepping through the generator and on each turn passing in the resolved value of the last promise), but it doesn't seem so.Vilhelmina
I
428

Break the chain

When you need to access the intermediate values in your chain, you should split your chain apart in those single pieces that you need. Instead of attaching one callback and somehow trying to use its parameter multiple times, attach multiple callbacks to the same promise - wherever you need the result value. Don't forget, a promise just represents (proxies) a future value! Next to deriving one promise from the other in a linear chain, use the promise combinators that are given to you by your library to build the result value.

This will result in a very straightforward control flow, clear composition of functionalities and therefore easy modularisation.

function getExample() {
    var a = promiseA(…);
    var b = a.then(function(resultA) {
        // some processing
        return promiseB(…);
    });
    return Promise.all([a, b]).then(function([resultA, resultB]) {
        // more processing
        return // something using both resultA and resultB
    });
}

Instead of the parameter destructuring in the callback after Promise.all that only became avail­able with ES6, in ES5 the then call would be replaced by a nifty helper method that was provided by many promise libraries (Q, Bluebird, when, …): .spread(function(resultA, resultB) { ….

Bluebird also features a dedicated join function to replace that Promise.all+spread combination with a simpler (and more efficient) construct:

…
return Promise.join(a, b, function(resultA, resultB) { … });
Intercellular answered 31/1, 2015 at 10:44 Comment(20)
Are the functions inside the array executed in order?Babysit
@scaryguy: There are no functions in the array, those are promises. promiseA and promiseB are the (promise-returning) functions here.Intercellular
Oh yes, actually I meant promises :) It might be a lack of my understanding of promises, sorry though. So when you use .all or .join, promises return values in order, right?Babysit
Yes, it's guaranteed that resultA comes from a and resultB comes from b, regardless of the order in which the results become available.Intercellular
The key for me was to actually return data from my Promise.all.then callback. Previously, I was returning the Promise.all callback, but I only had a console.log in my .then callback. This caused the data passed to any future promise callbacks to be undefined.Graehme
@Rhayene: Yes, Bluebird, but also Q, when and other major promise libs implement it. It's becoming less important with destructuring in ES6.Intercellular
ahhh, now it makes sense that it didn't compile :D I just misunderstood your answer. I got it running with the spread operator.Explain
what if its in a for loop? how to handle arguments of .spread(function(resultA, resultB) ?Heteronomy
@Heteronomy What is in a loop? This questions and all the answers assume that you have a fixed number of promises whose results you want to access individually. If you're working with arrays of arbitrary size, use the result array that Promise.all generates.Intercellular
@Intercellular my problem is I dont know how many promises are gonna be. there is a big chain of promises I have to handle and create elastic search query for each. what I did for now is when I return the response, I attach the data I need for the next query like return {response:response, data1:"d1",data2:"d2"} and access it in the then(function(data)) from response but it got very ugly.Heteronomy
@Heteronomy You might want to ask a separate question and include your code there. But in general, when you don't know how many elements you will have, use arrays.Intercellular
@Roland Never said it was :-) This answer was written in the ES5 age where no promises were in the standard at all, and spread was super useful in this pattern. For more modern solutions see the accepted answer. However, I already updated the explicit-passthrough answer, and there's really no good reason not to update this one as well.Intercellular
Any advice on naming promises when you store the result of Promise.all in a variable, so it's re-usable? Especially, if it is essentially just to make multiple values available to later code (as in this answer).Tailrace
Instead of Promise.all you can also use liftP2 et al: liftP2 = f => p => q => p.then(x => q.then(y => f(x, y))).Rayborn
@reify No, you shouldn't do that, it would bring trouble with rejections.Intercellular
I do not understand this example. If there is a chain of 'then' statements that require that values be propagated throughout the chain, I do not see how this solves the problem. A Promise that requires a previous value CANNOT be fired (created) until that value is present. Furthermore, Promise.all() simply waits for all promises in its list to finish: it does not impose an order. So I need each 'next' function to have access to all previous values and I don't see how your example does that. You should walk us through your example, because I do not believe it or understand it.Plath
@DavidSpector It still is a chain, the callback being chained after promiseB() chained to promiseA(), imposing the order. By storing the individual promises in variables, using Promise.all does let us depend on values from multiple steps of the chain instead of just the last one.Intercellular
I see. Well, I've since learned that the new Async/Await syntax includes automatic binding of arguments, so all the arguments are available to all the asynchronous functions. Having worked so hard to discover how to use 'bind', which doesn't require using Promise.all(), I've decided to abandon Promises entirely.Plath
@Intercellular I may have been reading this post and MDN for too long, but are PromiseA and PromiseB actually promises themselves? Are they not functions that return objects, which are promises themselves. And then those promises either result in the resolve or reject methods of the objects being executed? It's the final piece of the puzzle I am still confused about.Colin
@TeeJ Sorry, yes, promiseA is a function returning a promise and promiseA() is the actual promise. How these are fulfilled or rejected doesn't matter for the purposes of this question. If you're still unclear about resolve and reject being methods, please ask a new question where I answer in more detail.Intercellular
I
269

ECMAScript Harmony

Of course, this problem was recognized by the language designers as well. They did a lot of work and the async functions proposal finally made it into

ECMAScript 8

You don't need a single then invocation or callback function anymore, as in an asynchronous function (that returns a promise when being called) you can simply wait for promises to resolve directly. It also features arbitrary control structures like conditions, loops and try-catch-clauses, but for the sake of convenience we don't need them here:

async function getExample() {
    var resultA = await promiseA(…);
    // some processing
    var resultB = await promiseB(…);
    // more processing
    return // something using both resultA and resultB
}

ECMAScript 6

While we were waiting for ES8, we already did use a very similar kind of syntax. ES6 came with generator functions, which allow breaking the execution apart in pieces at arbitrarily placed yield keywords. Those slices can be run after each other, independently, even asynchronously - and that's just what we do when we want to wait for a promise resolution before running the next step.

There are dedicated libraries (like co or task.js), but also many promise libraries have helper functions (Q, Bluebird, when, …) that do this async step-by-step execution for you when you give them a generator function that yields promises.

var getExample = Promise.coroutine(function* () {
//               ^^^^^^^^^^^^^^^^^ Bluebird syntax
    var resultA = yield promiseA(…);
    // some processing
    var resultB = yield promiseB(…);
    // more processing
    return // something using both resultA and resultB
});

This did work in Node.js since version 4.0, also a few browsers (or their dev editions) did support generator syntax relatively early.

ECMAScript 5

However, if you want/need to be backward-compatible you cannot use those without a transpiler. Both generator functions and async functions are supported by the current tooling, see for example the documentation of Babel on generators and async functions.

And then, there are also many other compile-to-JS languages that are dedicated to easing asynchronous programming. They usually use a syntax similar to await, (e.g. Iced CoffeeScript), but there are also others that feature a Haskell-like do-notation (e.g. LatteJs, monadic, PureScript or LispyScript).

Intercellular answered 31/1, 2015 at 10:43 Comment(7)
@Intercellular do you need to await the async function examle getExample() from outside code?Hotbox
@arisalexis: Yes, getExample is still a function that returns a promise, working just like the functions in the other answers, but with nicer syntax. You could await a call in another async function, or you could chain .then() to its result.Intercellular
I'm curious, why did you answer your own question immediately after asking it? There is some good discussion here, but I'm curious. Maybe you found your answers on your own after asking?Helve
@granmoe: I posted the whole discussion on purpose as a canonical duplicate targetIntercellular
Is there a (not too laborious) way to avoid using Promise.coroutine (i.e., not using Bluebird or another library, but only plain JS) in the ECMAScript 6 example with the generator function? I had in mind something like steps.next().value.then(steps.next)... but that didn't work.Vilhelmina
@DanielMH You can do it manually without any helper function, but that is always laborious and error-prone. I can absolutely not recommend it. But unless you cannot use a transpiler, you'd be using async/await anyway.Intercellular
@Intercellular Thanks for the quick answer! I had hoped there would be a 'simple' solution (like the one I indicated: stepping through the generator and on each turn passing in the resolved value of the last promise), but it doesn't seem so.Vilhelmina
R
104

Synchronous inspection

Assigning promises-for-later-needed-values to variables and then getting their value via synchronous inspection. The example uses bluebird's .value() method but many libraries provide similar method.

function getExample() {
    var a = promiseA(…);

    return a.then(function() {
        // some processing
        return promiseB(…);
    }).then(function(resultB) {
        // a is guaranteed to be fulfilled here so we can just retrieve its
        // value synchronously
        var aValue = a.value();
    });
}

This can be used for as many values as you like:

function getExample() {
    var a = promiseA(…);

    var b = a.then(function() {
        return promiseB(…)
    });

    var c = b.then(function() {
        return promiseC(…);
    });

    var d = c.then(function() {
        return promiseD(…);
    });

    return d.then(function() {
        return a.value() + b.value() + c.value() + d.value();
    });
}
Runofthemine answered 31/1, 2015 at 13:16 Comment(3)
This is my favourite answer: readable, extensible and minimal reliance on library or language featuresRadom
@Jason: Uh, "minimal reliance on library features"? Synchronous inspection is a library feature, and a quite non-standard one to boot.Intercellular
I think he meant library specific featuresSero
I
62

Nesting (and) closures

Using closures for maintaining the scope of variables (in our case, the success callback function parameters) is the natural JavaScript solution. With promises, we can arbitrarily nest and flatten .then() callbacks - they are semantically equivalent, except for the scope of the inner one.

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return promiseB(…).then(function(resultB) {
            // more processing
            return // something using both resultA and resultB;
        });
    });
}

Of course, this is building an indentation pyramid. If indentation is getting too large, you still can apply the old tools to counter the pyramid of doom: modularize, use extra named functions, and flatten the promise chain as soon as you don't need a variable any more.
In theory, you can always avoid more than two levels of nesting (by making all closures explicit), in practise use as many as are reasonable.

function getExample() {
    // preprocessing
    return promiseA(…).then(makeAhandler(…));
}
function makeAhandler(…)
    return function(resultA) {
        // some processing
        return promiseB(…).then(makeBhandler(resultA, …));
    };
}
function makeBhandler(resultA, …) {
    return function(resultB) {
        // more processing
        return // anything that uses the variables in scope
    };
}

You can also use helper functions for this kind of partial application, like _.partial from Underscore/lodash or the native .bind() method, to further decrease indentation:

function getExample() {
    // preprocessing
    return promiseA(…).then(handlerA);
}
function handlerA(resultA) {
    // some processing
    return promiseB(…).then(handlerB.bind(null, resultA));
}
function handlerB(resultA, resultB) {
    // more processing
    return // anything that uses resultA and resultB
}
Intercellular answered 31/1, 2015 at 10:42 Comment(2)
This same suggestion is given as the solution to 'Advanced mistake #4' in Nolan Lawson's article on promises pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html. It's a good read.Benedicto
This is exactly the bind function in Monads. Haskell provides syntactic sugar (do-notation) to make it look like async/await syntax.Antiphon
I
59

Explicit pass-through

Similar to nesting the callbacks, this technique relies on closures. Yet, the chain stays flat - instead of passing only the latest result, some state object is passed for every step. These state objects accumulate the results of the previous actions, handing down all values that will be needed later again plus the result of the current task.

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return promiseB(…).then(b => [resultA, b]); // function(b) { return [resultA, b] }
    }).then(function([resultA, resultB]) {
        // more processing
        return // something using both resultA and resultB
    });
}

Here, that little arrow b => [resultA, b] is the function that closes over resultA, and passes an array of both results to the next step. Which uses parameter destructuring syntax to break it up in single variables again.

Before destructuring became available with ES6, a nifty helper method called .spread() was pro­vi­ded by many promise libraries (Q, Bluebird, when, …). It takes a function with multiple parameters - one for each array element - to be used as .spread(function(resultA, resultB) { ….

Of course, that closure needed here can be further simplified by some helper functions, e.g.

function addTo(x) {
    // imagine complex `arguments` fiddling or anything that helps usability
    // but you get the idea with this simple one:
    return res => [x, res];
}

…
return promiseB(…).then(addTo(resultA));

Alternatively, you can employ Promise.all to produce the promise for the array:

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return Promise.all([resultA, promiseB(…)]); // resultA will implicitly be wrapped
                                                    // as if passed to Promise.resolve()
    }).then(function([resultA, resultB]) {
        // more processing
        return // something using both resultA and resultB
    });
}

And you might not only use arrays, but arbitrarily complex objects. For example, with _.extend or Object.assign in a different helper function:

function augment(obj, name) {
    return function (res) { var r = Object.assign({}, obj); r[name] = res; return r; };
}

function getExample() {
    return promiseA(…).then(function(resultA) {
        // some processing
        return promiseB(…).then(augment({resultA}, "resultB"));
    }).then(function(obj) {
        // more processing
        return // something using both obj.resultA and obj.resultB
    });
}

While this pattern guarantees a flat chain and explicit state objects can improve clarity, it will become tedious for a long chain. Especially when you need the state only sporadically, you still have to pass it through every step. With this fixed interface, the single callbacks in the chain are rather tightly coupled and inflexible to change. It makes factoring out single steps harder, and callbacks cannot be supplied directly from other modules - they always need to be wrapped in boilerplate code that cares about the state. Abstract helper functions like the above can ease the pain a bit, but it will always be present.

Intercellular answered 31/1, 2015 at 10:42 Comment(14)
First, I don't think the syntax omitting the Promise.all should be encouraged (it won't work in ES6 when destructuring will replace it and switching a .spread to a then gives people often unexpected results. As of augment - I'm not sure why you need to use augment - adding things to the promise prototype is not an acceptable way to extend ES6 promises anyway which are supposed to be extended with (the currently unsupported) subclassing.Solangesolano
@BenjaminGruenbaum: What do you mean by "syntax omitting Promise.all"? None of the methods in this answer will break with ES6. Switching a spread to a destructuring then should not have issues either. Re .prototype.augment: I knew someone would notice it, I just liked to explore possibilities - going to edit it out.Intercellular
By the array syntax I mean return [x,y]; }).spread(... instead of return Promise.all([x, y]); }).spread(... which would not change when swapping spread for es6 destructuring sugar and would also not be a weird edge case where promises treat returning arrays differently from everything else.Solangesolano
@BenjaminGruenbaum: x and y are plain values here, there's no reason to wrap them within Promise.all. Are there any weird promise implementations out there that treat then callbacks which return arrays differently?Intercellular
Oh, nevermind - and yes. The above ` return promiseB(…).then(function(b) { return [resultA, b] });` can be written as return [resultA, promiseB(...)]; if the next thing you do is .spread it - I don't remember if bluebird still does this but it definitely did. Also, it could also always can be rewritten as ` return Promise.all[resultA, promiseB(...)] });`Solangesolano
Oh right, this could be employed here. I never liked those implicit to-promise casts :-) I'll extend the answer with an .all version.Intercellular
@BenjaminGruenbaum .spread() is same as .all().then(function(args){apply args...}); - because of the .all() you don't need Promise.all when using .spread() the method.Runofthemine
@Runofthemine right, but I think that that behavior is tricky and problematic for when people will use .then with destructuring instead of .spread, the fact .spread does .all is the oddball here imo.Solangesolano
@BenjaminGruenbaum Doesn't really matter as .spread is not really needed in 3.0 - multi-arg callback functions resolve only with the first arg by default. Although it's used in the above answer it's by far the most awkward of the alternatives.Runofthemine
This is probably the best answer. Promises are "Functional Reactive Programming"-light, and this is often the solution employed. For example, BaconJs, has #combineTemplate that allows you to combine results into an object that gets passed down the chainAcrocarpous
@UAvalos: Well combineTemplate is more like Promise.join (from the "break the chain" answer), isn't it?Intercellular
the standard Promise in javascript doesn't have .spread, mentioning it instead of Promise.all and destructuring makes this answer harder than it needed to be.Cisneros
@CapiEtheriel The answer was written when ES6 wasn't as wide-spread as it is today. Yeah, maybe it's time to swap the examplesIntercellular
@UAvalos While I agree with this being the best answer (from a FP perspective), I disagree with your comparison. FRP is about events, behaviors, event streams and suitable combinators. The only overlap with Promises it that both happen to deal with asynchronous control flows and share a "monad-like" structure.Blaineblainey
I
37

Mutable contextual state

The trivial (but inelegant and rather errorprone) solution is to just use higher-scope variables (to which all callbacks in the chain have access) and write result values to them when you get them:

function getExample() {
    var resultA;
    return promiseA(…).then(function(_resultA) {
        resultA = _resultA;
        // some processing
        return promiseB(…);
    }).then(function(resultB) {
        // more processing
        return // something using both resultA and resultB
    });
}

Instead of many variables one might also use an (initially empty) object, on which the results are stored as dynamically created properties.

This solution has several drawbacks:

  • Mutable state is ugly, and global variables are evil.
  • This pattern doesn't work across function boundaries, modularising the functions is harder as their declarations must not leave the shared scope
  • The scope of the variables does not prevent to access them before they are initialized. This is especially likely for complex promise constructions (loops, branching, excptions) where race conditions might happen. Passing state explicitly, a declarative design that promises encourage, forces a cleaner coding style which can prevent this.
  • One must choose the scope for those shared variables correctly. It needs to be local to the executed function to prevent race conditions between multiple parallel invocations, as would be the case if, for example, state was stored on an instance.

The Bluebird library encourages the use of an object that is passed along, using their bind() method to assign a context object to a promise chain. It will be accessible from each callback function via the otherwise unusable this keyword. While object properties are more prone to undetected typos than variables, the pattern is quite clever:

function getExample() {
    return promiseA(…)
    .bind({}) // Bluebird only!
    .then(function(resultA) {
        this.resultA = resultA;
        // some processing
        return promiseB(…);
    }).then(function(resultB) {
        // more processing
        return // something using both this.resultA and resultB
    }).bind(); // don't forget to unbind the object if you don't want the
               // caller to access it
}

This approach can be easily simulated in promise libraries that do not support .bind (although in a somewhat more verbose way and cannot be used in an expression):

function getExample() {
    var ctx = {};
    return promiseA(…)
    .then(function(resultA) {
        this.resultA = resultA;
        // some processing
        return promiseB(…);
    }.bind(ctx)).then(function(resultB) {
        // more processing
        return // something using both this.resultA and resultB
    }.bind(ctx));
}
Intercellular answered 31/1, 2015 at 10:43 Comment(11)
.bind() is unnecessary for preventing memory leakRunofthemine
@Esailija: But doesn't the returned promise hold a reference to the context object otherwise? OK, of course garbage collection will handle it later; it's not a "leak" unless the promise is never disposed.Intercellular
Yes but promises also hold reference to their fulfillment values and error reasons... but nothing holds reference to the promise so it doesn't matterRunofthemine
I was more in fear that e.g some ajax().then(function(res) { this.response = res; return res.length; }) promise was cached, where only the length of the response (a small value) is of interest but the whole response (a large value) is held in memory. Sure, it's not a "memory leak" of the kind "circular reference could confuse the engine gc" (as we knew from IE), but just a "leaks data to the caller".Intercellular
Please break this answer into two as I almost voted on the preamble! I think "the trivial (but inelegant and rather errorprone) solution" is the cleanest and simplest solution, since it relies no more on closures and mutable state than your accepted self-answer, yet is simpler. Closures are neither global nor evil. The arguments given against this approach make no sense to me given the premise. What modularization problems can there be given a "wonderful long flat promise chain"?Swirsky
@jib: I'm not sure into which parts I should break this. It covers the use of a mutable state that is accessed through closure - not local to each callback (yeah it's not the global scope, but I didn't want to introduce the term "free variable"). Notice that I didn't criticise closures, but the mutations. The problems with modularisation ensue when your promise chain is getting too long, and your callbacks are getting too large, so that you want to put them aside.Intercellular
As I said above, Promises are "Functional Reactive Programming"-light. This is an anti-pattern in FRPAcrocarpous
@UAvalos: Please downvote the pattern - I cannot do it myself :-)Intercellular
I'm not sure calling this "mutable state" is quite accurate, as the goal of promises in the first place (or at least a main one) is to allow async code to be written the way sync code would be, in which case we're talking about local variables. Certainly no-one considers assigning a value to a local variable to be violating the mutable state principal, do they?Westing
@dmansfield: Yeah, maybe, but the problem with this solution is that a) the variable is not really local b) there is no temporal dead zone like it would be above a let - and in asynchronous code you're much more likely to access it before you initialise it.Intercellular
When you are using named functions which are defined out of scope, the 'bind(ctx) solution' is actually quite handy. Example: var ctx = {lightOn: true); getBrightnessAsync().then(calculateNewBrightness.bind(ctx)).then(setBrightnessAsync).then(sendResponseAsync.bind(ctx)).Vilhelmina
C
23

A less harsh spin on "Mutable contextual state"

Using a locally scoped object to collect the intermediate results in a promise chain is a reasonable approach to the question you posed. Consider the following snippet:

function getExample(){
    //locally scoped
    const results = {};
    return promiseA(paramsA).then(function(resultA){
        results.a = resultA;
        return promiseB(paramsB);
    }).then(function(resultB){
        results.b = resultB;
        return promiseC(paramsC);
    }).then(function(resultC){
        //Resolve with composite of all promises
        return Promise.resolve(results.a + results.b + resultC);
    }).catch(function(error){
        return Promise.reject(error);
    });
}
  • Global variables are bad, so this solution uses a locally scoped variable which causes no harm. It is only accessible within the function.
  • Mutable state is ugly, but this does not mutate state in an ugly manner. The ugly mutable state traditionally refers to modifying the state of function arguments or global variables, but this approach simply modifies the state of a locally scoped variable that exists for the sole purpose of aggregating promise results...a variable that will die a simple death once the promise resolves.
  • Intermediate promises are not prevented from accessing the state of the results object, but this does not introduce some scary scenario where one of the promises in the chain will go rogue and sabotage your results. The responsibility of setting the values in each step of the promise is confined to this function and the overall result will either be correct or incorrect...it will not be some bug that will crop up years later in production (unless you intend it to!)
  • This does not introduce a race condition scenario that would arise from parallel invocation because a new instance of the results variable is created for every invocation of the getExample function.

Example is available on jsfiddle

Crepitate answered 24/3, 2017 at 20:8 Comment(4)
At least avoid the Promise constructor antipattern!Intercellular
Thanks @Bergi, I didn't even realize that was an anti-pattern until you mentioned it!Crepitate
this is a good workaround to mitigate promise related error.I was using ES5 and did not want to add another library to work with promise.Turcotte
Reasoning in the details rather than the rules of thumb to cmeSonjasonnet
J
7

Node 7.4 now supports async/await calls with the harmony flag.

Try this:

async function getExample(){

  let response = await returnPromise();

  let response2 = await returnPromise2();

  console.log(response, response2)

}

getExample()

and run the file with:

node --harmony-async-await getExample.js

Simple as can be!

Justen answered 21/1, 2017 at 22:14 Comment(0)
J
6

Another answer, using babel-node version <6

Using async - await

npm install -g [email protected]

example.js:

async function getExample(){

  let response = await returnPromise();

  let response2 = await returnPromise2();

  console.log(response, response2)

}

getExample()

Then, run babel-node example.js and voila!

Justen answered 20/11, 2015 at 19:59 Comment(2)
Yes I did, right after I posted mine. Still, I'm going to leave it because it explains how to actually get up and running with using ES7 as opposed to just saying that someday ES7 will be available.Justen
Oh right, I should update my answer to say that the "experimental" plugins for these are already here.Intercellular
M
5

This days, I also hava meet some questions like you. At last, I find a good solution with the quesition, it's simple and good to read. I hope this can help you.

According to how-to-chain-javascript-promises

ok, let's look at the code:

const firstPromise = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('first promise is completed');
            resolve({data: '123'});
        }, 2000);
    });
};

const secondPromise = (someStuff) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('second promise is completed');
            resolve({newData: `${someStuff.data} some more data`});
        }, 2000);
    });
};

const thirdPromise = (someStuff) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('third promise is completed');
            resolve({result: someStuff});
        }, 2000);
    });
};

firstPromise()
    .then(secondPromise)
    .then(thirdPromise)
    .then(data => {
        console.log(data);
    });
Mabe answered 25/7, 2017 at 6:34 Comment(4)
This doesn't really answer the question about how to access previous results in the chain.Intercellular
Every promise can get the previous value, what's your meaning?Mabe
Take a look at the code in the question. The aim is not to get the result of the promise that .then is called on, but results from before that. E.g. thirdPromise accessing the result of firstPromise.Intercellular
This is the default behaviour of promises, I'm afraid this doesn't reply the original question, sorry.Insurrectionary
J
2

I am not going to use this pattern in my own code since I'm not a big fan of using global variables. However, in a pinch it will work.

User is a promisified Mongoose model.

var globalVar = '';

User.findAsync({}).then(function(users){
  globalVar = users;
}).then(function(){
  console.log(globalVar);
});
Justen answered 11/8, 2015 at 18:35 Comment(3)
Notice that this pattern is already detailed in the Mutable contextual state answer (and also why it is ugly - I'm not a big fan either)Intercellular
In your case, the pattern seems to be useless though. You don't need a globalVar at all, just do User.findAsync({}).then(function(users){ console.log(users); mongoose.connection.close() });?Intercellular
I don't need it personally in my own code, but the user may need to run more async in the second function and then interact with the original Promise call. But like mentioned, I'll be using generators in this case. :)Justen
P
2

Another answer, using sequential executor nsynjs:

function getExample(){

  var response1 = returnPromise1().data;

  // promise1 is resolved at this point, '.data' has the result from resolve(result)

  var response2 = returnPromise2().data;

  // promise2 is resolved at this point, '.data' has the result from resolve(result)

  console.log(response, response2);

}

nynjs.run(getExample,{},function(){
    console.log('all done');
})

Update: added working example

function synchronousCode() {
     var urls=[
         "https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js",
         "https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js",
         "https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"
     ];
     for(var i=0; i<urls.length; i++) {
         var len=window.fetch(urls[i]).data.text().data.length;
         //             ^                   ^
         //             |                   +- 2-nd promise result
         //             |                      assigned to 'data'
         //             |
         //             +-- 1-st promise result assigned to 'data'
         //
         console.log('URL #'+i+' : '+urls[i]+", length: "+len);
     }
}

nsynjs.run(synchronousCode,{},function(){
    console.log('all done');
})
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
Parabolize answered 10/6, 2017 at 0:56 Comment(0)
S
1

When using bluebird, you can use .bind method to share variables in promise chain:

somethingAsync().bind({})
.spread(function (aValue, bValue) {
    this.aValue = aValue;
    this.bValue = bValue;
    return somethingElseAsync(aValue, bValue);
})
.then(function (cValue) {
    return this.aValue + this.bValue + cValue;
});

please check this link for further information:

http://bluebirdjs.com/docs/api/promise.bind.html

Sweeney answered 12/6, 2016 at 6:33 Comment(1)
Notice that this pattern is already detailed in the Mutable contextual state answerIntercellular
S
1
function getExample() {
    var retA, retB;
    return promiseA(…).then(function(resultA) {
        retA = resultA;
        // Some processing
        return promiseB(…);
    }).then(function(resultB) {
        // More processing
        //retA is value of promiseA
        return // How do I gain access to resultA here?
    });
}

easy way :D

Sanitize answered 3/3, 2017 at 9:45 Comment(1)
You've noticed this answer?Intercellular
S
1

I think you can use hash of RSVP.

Something like as below :

    const mainPromise = () => {
        const promise1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('first promise is completed');
                resolve({data: '123'});
            }, 2000);
        });

        const promise2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('second promise is completed');
                resolve({data: '456'});
            }, 2000);
        });

        return new RSVP.hash({
              prom1: promise1,
              prom2: promise2
          });

    };


   mainPromise()
    .then(data => {
        console.log(data.prom1);
        console.log(data.prom2);
    });
Slapstick answered 29/8, 2017 at 10:34 Comment(1)
Yes, that's the same as the Promise.all solution, only with an object instead of an array.Intercellular
P
0

Solution:

You can put intermediate values in scope in any later 'then' function explicitly, by using 'bind'. It is a nice solution that doesn't require changing how Promises work, and only requires a line or two of code to propagate the values just like errors are already propagated.

Here is a complete example:

// Get info asynchronously from a server
function pGetServerInfo()
    {
    // then value: "server info"
    } // pGetServerInfo

// Write into a file asynchronously
function pWriteFile(path,string)
    {
    // no then value
    } // pWriteFile

// The heart of the solution: Write formatted info into a log file asynchronously,
// using the pGetServerInfo and pWriteFile operations
function pLogInfo(localInfo)
    {
    var scope={localInfo:localInfo}; // Create an explicit scope object
    var thenFunc=p2.bind(scope); // Create a temporary function with this scope
    return (pGetServerInfo().then(thenFunc)); // Do the next 'then' in the chain
    } // pLogInfo

// Scope of this 'then' function is {localInfo:localInfo}
function p2(serverInfo)
    {
    // Do the final 'then' in the chain: Writes "local info, server info"
    return pWriteFile('log',this.localInfo+','+serverInfo);
    } // p2

This solution can be invoked as follows:

pLogInfo("local info").then().catch(err);

(Note: a more complex and complete version of this solution has been tested, but not this example version, so it could have a bug.)

Plath answered 27/8, 2019 at 20:17 Comment(5)
This seems to be the same pattern as in the nesting (and) closures answerIntercellular
It does look similar. I've since learned that the new Async/Await syntax includes automatic binding of arguments, so all the arguments are available to all the asynchronous functions. I'm abandoning Promises.Plath
async/await still means using promises. What you might abandon is then calls with callbacks.Intercellular
is it just me, or is manually manipulating scope about 10x more primitive than manual memory management? why the heck is this necessary? it looks awful.Harleigh
It is actually quite clean to do things after an asynchronous operation like this: promise.then1.then2.then3.catch . And while you can only pass one value from one part of this chain to the next part, that value can be an array or object containing any number of subvalues! And it's even more natural if you use "async" functions, because await can be used to "wait" for each asynchronous operation to complete, without any promise chains! So asynchronous programming can be very elegant and compact.Plath
I
0

What I learn about promises is to use it only as return values avoid referencing them if possible. async/await syntax is particularly practical for that. Today all latest browsers and node support it: https://caniuse.com/#feat=async-functions , is a simple behavior and the code is like reading synchronous code, forget about callbacks...

In cases I do need to reference a promises is when creation and resolution happen at independent/not-related places. So instead an artificial association and probably an event listener just to resolve the "distant" promise, I prefer to expose the promise as a Deferred, which the following code implements it in valid es5

/**
 * Promise like object that allows to resolve it promise from outside code. Example:
 *
```
class Api {
  fooReady = new Deferred<Data>()
  private knower() {
    inOtherMoment(data=>{
      this.fooReady.resolve(data)
    })
  }
}
```
 */
var Deferred = /** @class */ (function () {
  function Deferred(callback) {
    var instance = this;
    this.resolve = null;
    this.reject = null;
    this.status = 'pending';
    this.promise = new Promise(function (resolve, reject) {
      instance.resolve = function () { this.status = 'resolved'; resolve.apply(this, arguments); };
      instance.reject = function () { this.status = 'rejected'; reject.apply(this, arguments); };
    });
    if (typeof callback === 'function') {
      callback.call(this, this.resolve, this.reject);
    }
  }
  Deferred.prototype.then = function (resolve) {
    return this.promise.then(resolve);
  };
  Deferred.prototype.catch = function (r) {
    return this.promise.catch(r);
  };
  return Deferred;
}());

transpiled form a typescript project of mine:

https://github.com/cancerberoSgx/misc-utils-of-mine/blob/2927c2477839f7b36247d054e7e50abe8a41358b/misc-utils-of-mine-generic/src/promise.ts#L31

For more complex cases I often use these guy small promise utilities without dependencies tested and typed. p-map has been useful several times. I think he covered most use cases:

https://github.com/sindresorhus?utf8=%E2%9C%93&tab=repositories&q=promise&type=source&language=

Illfavored answered 12/9, 2019 at 20:37 Comment(2)
Sounds like you are suggesting either mutable contextual state or synchronous inspection?Intercellular
@bergi First time I head those names.adding to the list thanks.I know this kind of self-aware promises by the name of Deferred - BTW the implementation is just a promise with resolve wrapped. I often need this pattern in those cases where the responsibility of promise creation and resolution are independent so there's no need for relate them just to resolve a promise. I adapted but not for your example, and using a class, but maybe equivalent.Illfavored

© 2022 - 2024 — McMap. All rights reserved.