【问题标题】:Fluid construction of parallel promises并行承诺的流畅构造
【发布时间】:2015-02-17 16:40:11
【问题描述】:

我的问题是关于 BlueBird 中优雅的 promise 并行化,当您需要将上下文和参数传递给构建 promise 的函数时。

为了让我的问题易于理解和测试,我做了一个不依赖的例子。

假设我进行涉及异步“计算机”(必须释放其资源)的计算( 1/(xxx) + 1/(x*x) )。正方形和立方体是异步独立计算的。

我可以这样计算:

InitComputer(2) // returns a promise
.then(invert)
.then(function(arg){
    return Promise.all([
        proto.square(arg),
        proto.cube(arg)
    ]);
}).spread(function(sq, cu){
    this.set(sq + cu);
}).catch(function(err){
    console.log('err:', err);
}).finally(endComputer);

但我发现 all 的这种用法与理论上可能的用法相比太重了。当您将函数作为参数传递给then 时,它会被执行。当您将函数传递给all 时,它们不是,有一个错误。我怀疑我缺少实用程序或模式...

有没有办法把它改成这种风格的更简单的东西:

InitComputer(2)
.then(invert)
.all([
    proto.square,
    proto.cube
]).spread(function(sq, cu){
    this.set(sq + cu);
}).catch(function(err){
    console.log('err:', err);
}).finally(endComputer);

?

我可能会破解 Promise.prototype.all 或定义一个新函数以避免增加多态性,但我只对不涉及修改我不拥有的对象的解决方案感兴趣。


附件:

以下是我的测试“计算机”的定义方式:

var Promise = require("bluebird");

function Computer(){}
function InitComputer(v){
    // initializing a computer is asynchronous and may fail hence the promise
    var c = new Computer(), resolver = Promise.defer();
    setTimeout(function(){
        if (v>1) resolver.resolve(v);
        else resolver.reject(new Error("bad value: "+v));
    },100);
    return resolver.promise.bind(c);
}
var proto = Computer.prototype;
proto.square = function(x){
    // imagine this really uses the computer and is asynchronous
    if (!this instanceof Computer) throw new Error('not a computer');
    return x*x
}
proto.cube = function(x){ return x*x*x }
proto.set = function(v){ this.value = v }

function endComputer(){
    // releases resources here
    console.log('value:', this.value);
}

// this asynchronous function doesn't involve or know the computer
function invert(v){ return 1/v }

【问题讨论】:

  • 我可能会出错,但在将结果传递给Promise.all 的数组构造函数之前,没有立即评估proto.square(arg)proto.cube(arg)? (编辑)哦,好吧,他们可能应该是异步的,没关系。
  • @Groo 没错。 “想象一下这真的使用计算机并且是异步的”。我做了最少的代码让问题可读,所以我不得不采取一些捷径..

标签: javascript node.js promise bluebird


【解决方案1】:

您不必在那里使用Promise.all。而不是这样做:

.then(function(arg){
    return Promise.all([
        proto.square(arg),
        proto.cube(arg)
    ]);
}).spread(...

你可以简单地使用:

.then(function(arg){
    return [proto.square(arg), proto.cube(arg)];
}).spread(...

如果我们在 node.js 中有箭头函数,它会很简单:

.then(arg => [proto.square(arg), proto.cube(arg)]).spread(...

Promise.all 用于当您需要开始至少有 2 个承诺的承诺链。例如:

var promise1 = somePromise();
var promise2 = somePromise2();

// Start the chain here
Promise.all([promise1, promise2])
.spread(function(value1, value2) {
    // ...
});

【讨论】:

  • 它并不像我希望的那么简单,但它真的可以接受。
  • 我想知道为什么all,与then相反,不执行参数中的函数。
  • @dystroy 哈哈,不过主要是关于使用this.squarethis.cube
【解决方案2】:

对于您提到的资源用例管理,bluebird 有Promise.using()Promise.using() 让您设置 disposer() 函数,以便在您完成使用后自动关闭异步检索的资源

Promise.join() 也有助于结合 cubesquare 异步方法的结果

在这里,我稍微重写了您的 InitComputer 示例以说明其工作原理 - 它现在返回 Computer 实例,并将 val 添加为属性,而不是 val,我还将 endComputer 放在原型上

注意:您始终可以像这样使用Promise.method() 而不是返回延迟:

var invert = Promise.method(function invert(v){ return 1/v })

新的 initComputer:

function InitComputer(v){
    var c = new Computer(), resolver = Promise.defer();
    setTimeout(function(){
        if (v>1) {
            c.val = v;
            resolver.resolve(c);
        }
        else resolver.reject(new Error("bad value: "+v));
    },100); /** notice resource disposer function added below **/
    return resolver.promise.bind(c).disposer(function(compu){compu.endComputer()});
}

新代码:

Promise.using(InitComputer(1.2), function(computer){
    return invert(computer.val)
    .then(function(inverted){
        return Promise.join(computer.square(inverted), computer.cube(inverted), 
            function(sq, cu){
                computer.set(sq + cu)
            }
        )
    })
    .catch(function(err){
        console.log('err:', err);
    });
})

【讨论】:

  • +1 但您正在回答一个已回答的旧问题,其中包含在提问时不可用的功能(如Promise.using)。重提这个老问题有点乱。请注意,BlueBird 的作者现在将 Promise.defer() 声明为反模式,因为他更喜欢更简单的 new Promise(f1,f2)
  • 谢谢你来晚了,但是这在我的搜索中排名第一,所以我想我会加 0.02 美元。我还找到了答案 here,这对这个主题也很有帮助
猜你喜欢
  • 1970-01-01
  • 2015-01-06
  • 1970-01-01
  • 2017-05-12
  • 2016-06-05
  • 1970-01-01
  • 2022-12-07
  • 2020-02-17
  • 1970-01-01
相关资源
最近更新 更多