最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Transform array of promises into array of values when fulfilled - Stack Overflow

programmeradmin2浏览0评论

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.

Share Improve this question asked Aug 30, 2013 at 6:58 OlegOleg 25k8 gold badges64 silver badges94 bronze badges 3
  • Could you explain your use case a bit more? What exactly does queue.enqueue achieve? Since you're already using a callback, what's wrong with 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:27
  • @DavidMcMullin: queue.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
  • I think Bergi has got it covered =) – Frances McMullin Commented Aug 30, 2013 at 11:34
Add a comment  | 

3 Answers 3

Reset to default 11

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

发布评论

评论列表(0)

  1. 暂无评论