在履行时将promise的数组转换为值数组

时间:2013-08-30 06:58:41

标签: javascript node.js promise q

我正在使用一个能够返回promise的已解析值的函数。优雅地失败肯定是一个奖励,但这是一个假定的前提条件,当函数被调用时,承诺已准备好解决。

虽然我正在使用允许类似于下面的队列操作的webdriver.js promise implementation,但我不希望在队列/链的语义等方面太丢失。仅仅因为这个原因,这里有一些伪代码涵盖我想要实现的目标:

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

注意:afaik Q.all()做我正在做的事情 - 它需要一系列承诺并返回一个数组的承诺,不是它的已解析值。我很高兴被证明是错的。

3 个答案:

答案 0 :(得分:3)

获取承诺的最终价值的唯一方法是使用then。如果函数异步执行工作,它必须返回一个promise,在任何情况下都不能返回一个普通值。要做到这一点,它必须阻止执行的线程,直到工作完成,这只能通过线程或光纤来实现,这会带来死锁和交错危险的危险。

因此,Q.all 实际上是您需要的方法,但要跟进then以获取最终值。

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

当然,还有办法欺骗。 promise.inspect()将返回描述承诺状态的对象,如{state: "fulfilled", value: value}已准备好,或{state: "rejected", error}如果失败,或{state: "pending"},如果尚未准备好。如果如您所说,保证outputs承诺已由Q.all返回,则可以执行以下操作:

outputs = outputs.inspect().value

我不推荐这个。了解承诺得到解决的最佳方法是使用then

如果您还可以通过某种外部方式保证所有outputs都已准备就绪,您也可以将值推送到outputs数组。

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;

然而,最好的方法是使用Q.all(outputs).then来确保在所有输出准备就绪后保证事件。

答案 1 :(得分:2)

由于您通常不知道承诺是否得到解决,因此您不能简单地将它们转换为普通值。 Q.all必须返回一个promise,因为它无法从异步上下文中提取values数组。唯一一次你知道一个promise有一个值就在成功处理程序中,无论如何你都会得到这个值。您不应该使用另一个事件系统来告诉您承诺何时结算 - 使用承诺本身。

所以而不是使用queue.enqueue,只需添加Q.all(outputs).then(function(values){ /* do something */ })。但是,如果您无法解决此问题,可以查看Promise inspect debugging method_.pluck(_.invoke(outputs, "inspect"), "value")。但请注意,那么将值存储在promises中可能会更容易。

答案 2 :(得分:2)

对于其他根据问题标题寻找答案的人,以下与ES 2017+结合使用以实现承诺数组并返回值数组:

var arrayOfValues = await Promise.all(arrayOfPromises)