Categories
bluebird es6-promise javascript promise scope

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

738

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

1

267

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).

7

  • @Bergi do you need to await the async function examle getExample() from outside code?

    Aug 28, 2015 at 17:51


  • @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.

    – Bergi

    Aug 28, 2015 at 21:12

  • 1

    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?

    – granmoe

    Mar 6, 2016 at 5:46

  • @granmoe: I posted the whole discussion on purpose as a canonical duplicate target

    – Bergi

    Mar 6, 2016 at 11:17

  • 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.

    Aug 31, 2016 at 13:03


267

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).

7

  • @Bergi do you need to await the async function examle getExample() from outside code?

    Aug 28, 2015 at 17:51


  • @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.

    – Bergi

    Aug 28, 2015 at 21:12

  • 1

    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?

    – granmoe

    Mar 6, 2016 at 5:46

  • @granmoe: I posted the whole discussion on purpose as a canonical duplicate target

    – Bergi

    Mar 6, 2016 at 11:17

  • 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.

    Aug 31, 2016 at 13:03


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

3

  • 6

    This is my favourite answer: readable, extensible and minimal reliance on library or language features

    – Jason

    Apr 15, 2016 at 1:21


  • 16

    @Jason: Uh, “minimal reliance on library features“? Synchronous inspection is a library feature, and a quite non-standard one to boot.

    – Bergi

    Jun 25, 2016 at 19:23

  • 2

    I think he meant library specific features

    Aug 16, 2017 at 14:53