Bergi Bergi - 7 months ago 12
Javascript Question

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

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

Answer

ECMAScript Harmony

Of course, this problem was recognized by the language designers as well.

And they are working on a solution which will probably come with ECMAScript 8: async functions. You won't need a single then invocation or callback function any more, as in an asynchronous function (that returns a promise when being called) you can simply wait for promises to resolve directly. It will also feature 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 are waiting for ES8, we already can use a very similar kind of syntax. ES6 comes with generator functions, which allow to break 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 already works in Node.js, also a few browsers (or their dev editions) do support generator syntax.

ECMAScript 5

However, if you want/need to be backwards-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 (by default) and async functions (experimental, included in the stage-3 preset).

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