I'm after a function that would return a resolved value of a promise. Failing gracefully is definitely a bonus, but it's an assumed precondition that when the function is called the promise is ready to be resolved.
While I'm working with webdriver.js promise implementation which allows the queue manipulations similar to below, I don't want to get too lost in semantics of queues/chains etc. For that reason alone, here is some pseudocode to cover what I'm trying to achieve:
var inputs = [...], outputs;
outputs = inputs.map(function(input){
//queue some async tasks to be performed with input
queue.enqueue(...);
//I can't return the *output* value here yet, naturally, so instead
return promise;
});
//now I'll add another task to the same queue
//this means that by the time this task is run
//the async tasks above would have been executed
//and the promises would be "resolvable"... right?
queue.enqueue(function(){
console.log(outputs); //>an array of promises
console.log(doSomeMagic(outputs)); //>resolved values as needed <<<
});
NB: afaik Q.all()
would not do what I'm after - it takes an array of promises and returns a promise of an array, not its resolved value. I'm only happy to be proved wrong.
I'm after a function that would return a resolved value of a promise. Failing gracefully is definitely a bonus, but it's an assumed precondition that when the function is called the promise is ready to be resolved.
While I'm working with webdriver.js promise implementation which allows the queue manipulations similar to below, I don't want to get too lost in semantics of queues/chains etc. For that reason alone, here is some pseudocode to cover what I'm trying to achieve:
var inputs = [...], outputs;
outputs = inputs.map(function(input){
//queue some async tasks to be performed with input
queue.enqueue(...);
//I can't return the *output* value here yet, naturally, so instead
return promise;
});
//now I'll add another task to the same queue
//this means that by the time this task is run
//the async tasks above would have been executed
//and the promises would be "resolvable"... right?
queue.enqueue(function(){
console.log(outputs); //>an array of promises
console.log(doSomeMagic(outputs)); //>resolved values as needed <<<
});
NB: afaik Q.all()
would not do what I'm after - it takes an array of promises and returns a promise of an array, not its resolved value. I'm only happy to be proved wrong.
3 Answers
Reset to default 11For anyone else looking for an answer based on the title of the question the following works with ES 2017+ to take an array of promises and return an array of values:
var arrayOfValues = await Promise.all(arrayOfPromises)
The only way to get the eventual value for a promise is with then
. If a function performs work asynchronously, it must return a promise and under no circumstances can it return a plain value. To do so, it would necessarily have to block the thread of execution until the work completes, which is only possible with threads or fibers, which entrain the perils of deadlock and interleaving hazards.
As such, Q.all
is in fact the method you need, except to follow up with then
to get the eventual value.
Q.all(inputs.map(function (input) {
return promiseForOutput; // however you go about this
}))
.then(function (outputs) {
// at this event, outputs is an array of output values
});
There are ways to cheat, of course. promise.inspect()
will return an object describing the state of the promise, like {state: "fulfilled", value: value}
if it’s ready, or {state: "rejected", error}
if it failed, or {state: "pending"}
, if it is not yet ready. If, as you say, you are guaranteed that the outputs
promise, returned by Q.all
has been fulfilled, you can do this:
outputs = outputs.inspect().value
I don’t recommend this. The best way to know that a promise is resolved is to use then
.
You could also just push values onto an outputs
array of your making if you can also guarantee that all the outputs
are ready through some external means.
var endResult = Q.defer();
var outputs = [];
inputs.forEach(function (input) {
outputPromise.then(function (output) {
outputs.push(output);
check();
}, endResult.reject);
});
check();
function check() {
if (outputs.length === inputs.length) {
// manipulate outputs directly, they are ready
endResult.resolve();
}
}
return endResult.promise;
The best means, however, is to just use Q.all(outputs).then
to get an event that is guaranteed to be after all the outputs are ready.
Since you in general never know whether promises are resolved or not, you cannot simply transform them into a plain value. Q.all
must return a promise since it cannot extract the values array from the async context. The only time you know that a promise has a value is in the success handler, and there you're getting the value anyway. You should not use another event system that tells you when a promise has settled - use the promise itself.
So instead of using queue.enqueue
, just put Q.all(outputs).then(function(values){ /* do something */ })
. However, if you cannot work around that, you might have a look at the Promise inspect
debugging method: _.pluck(_.invoke(outputs, "inspect"), "value")
. But notice that it might be easier not to store the values in promises then.
Q.all(outputs).then(function(values){console.log(values);})
? I'm not sure about WebDriver promises but in general, promises prohibit synchronous access (yes, even after they've been resolved). – Frances McMullin Commented Aug 30, 2013 at 8:27queue.enqueue
is pseudo-code for a method that adds a new promise to a "stack" of promises that are to be sequentially executed. As for the second part of your comment, would you provide this as an answer with more detail/references? Abstractly, being able to access a resolved promise's value still seems perfectly reasonable [to me]. – Oleg Commented Aug 30, 2013 at 10:34