How do you properly return multiple values from a Promise?
I've recently run into a certain situation a couple of times, which I didn't know how to solve properly. Assume the following code:
somethingAsync()
.then( afterSomething )
.then( afterSomethingElse )
function afterSomething( amazingData ) {
return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
}
Now a situation might arise where I would want to have access to amazingData
in afterSomethingElse
.
One obvious solution would be to return an array or a hash from afterSomething
, because, well, you can only return one value from a function. But I'm wondering if there is a way to have afterSomethingElse
accept 2 parameters and invoke it likewise, as that seems a lot easier to document and understand.
I'm only wondering about this possibility since there is Q.spread
, which does something similar to what I want.
Solution 1:
You can't resolve a promise with multiple properties just like you can't return multiple values from a function. A promise conceptually represents a value over time so while you can represent composite values you can't put multiple values in a promise.
A promise inherently resolves with a single value - this is part of how Q works, how the Promises/A+ spec works and how the abstraction works.
The closest you can get is use Q.spread
and return arrays or use ES6 destructuring if it's supported or you're willing to use a transpilation tool like BabelJS.
As for passing context down a promise chain please refer to Bergi's excellent canonical on that.
Solution 2:
you can only pass one value, but it can be an array with multiples values within, as example:
function step1(){
let server = "myserver.com";
let data = "so much data, very impresive";
return Promise.resolve([server, data]);
}
on the other side, you can use the destructuring expression for ES2015 to get the individual values.
function step2([server, data]){
console.log(server); // print "myserver.com"
console.log(data); // print "so much data, very impresive"
return Promise.resolve("done");
}
to call both promise, chaining them:
step1()
.then(step2)
.then((msg)=>{
console.log(msg); // print "done"
})
Solution 3:
You can return an object containing both values — there's nothing wrong with that.
Another strategy is to keep the value, via closures, instead of passing it through:
somethingAsync().then(afterSomething);
function afterSomething(amazingData) {
return processAsync(amazingData).then(function (processedData) {
// both amazingData and processedData are in scope here
});
}
Fully rather than partially inlined form (equivalent, arguably more consistent):
somethingAsync().then(function (amazingData) {
return processAsync(amazingData).then(function (processedData) {
// both amazingData and processedData are in scope here
});
}