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?
});
}
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:
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:
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 unusablethis
keyword. While object properties are more prone to undetected typos than variables, the pattern is quite clever: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):
When using bluebird, you can use
.bind
method to share variables in promise chain:please check this link for further information:
http://bluebirdjs.com/docs/api/promise.bind.html
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 any more, 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: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 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.
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 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 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-likedo
-notation (e.g. LatteJs, monadic, PureScript or LispyScript).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.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.
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:Another answer, using sequential executor nsynjs:
Update: added working example
easy way :D