【问题标题】:How do you avoid the promise constructor antipattern with Promise.all你如何避免使用 Promise.all 的 Promise 构造函数反模式
【发布时间】:2015-11-19 18:24:10
【问题描述】:

在使用多个 Promise 和 Promise.all 时如何避免 Promise 构造函数反模式?

假设我有以下代码:

getFoo = function() {
    return new Promise(function(resolve, reject) {
        var promises = [];
        promises.push(new Promise(function(resolve, reject) => {
            getBar1().then(function(bar1) {
                processBar1(bar1); 
                resolve(bar1);
            });
        }));
        promises.push(new Promise(function(resolve, reject) => {
            getBar2().then(function(bar2) {
                processBar2(bar2); 
                resolve(bar2);
            });
        }));
        Promise.all(promises).spread(function(bar1, bar2) {
            var result = processBothBars(bar1, bar2);
            resolve(result);
        });
    });
}

它提出了反模式的一些基本问题,错误被吞噬,以及厄运金字塔。

顺便说一句,我正在使用蓝鸟。

【问题讨论】:

  • 而不是顶部的return new Promise(..return Promise.all(...?
  • @KevinB 但内部承诺仍会吞下错误
  • 为什么需要内在的承诺?为什么不能将 getBar1() 的返回值推送到数组?我对 bluebird 不太熟悉,但是,如果它遵循原生 Promise 功能,那么上面的 sn-p 中根本不需要 new Promise

标签: javascript node.js promise ecmascript-6 bluebird


【解决方案1】:

Fwiw bluebird 在这方面证明了一些糖:

getFoo = function() {
     return Promise.join(getBar1().tap(processBar1),
                         getBar2().tap(processBar2),
                         processBothBars);
}

【讨论】:

  • 数组在那里做什么?
  • @Bergi 从我没有注意到需要 processBothBars 的时候就偷偷溜进来了,所以它是 .all
【解决方案2】:

无需在此处创建您自己的任何 Promise,因为 getBar1()getBar2() 都已经返回 Promise - 至少我们假设是这样,因为两者都是 thenable。

提供processBar1processBar2分别返回你感兴趣的结果,代码会简化如下:

var getFoo = function() {
    // write promises as an array literal
    var promises = [
        getBar1().then(processBar1),//result returned by getBar1() is automatically passed to processBar1
        getBar2().then(processBar2) // ... ditto ...
    ];
    return Promise.all(promises).spread(processBothBars);
};

【讨论】:

  • 我不明白apply 的作用。为什么不简化为.spread(processBothBars)
  • @Bergi,我希望我能想到你的 .spread(processBothBars) 事情。我承认我的思想不是特别分散。
【解决方案3】:

你可以一起摆脱new Promise

getFoo = function() {
    var promises = [];
    promises.push(getBar1().then(function(bar1) {
        processBar1(bar1);
        return bar1;
    }));
    promises.push(getBar2().then(function(bar2) {
        processBar2(bar2);
        return bar2;
    }));
    return Promise.all(promises).spread(function(bar1, bar2) {
        var result = processBothBars(bar1, bar2);
        return result;
    });
}

// start mock
function getBar1() {
    return Promise.resolve({name:'bar1',processed: false});
}
function getBar2() {
    return Promise.resolve({name:'bar2',processed: false});
}
function processBar1(bar1) {
  bar1.processed = true;
}
function processBar2(bar2) {
  bar2.processed = true;
}
function processBothBars (bar1, bar2) {
  return [bar1, bar2].filter(function (bar) {
    return bar.processed;
  }).map(function (bar) {
    return bar.name;
  });
}
Promise.prototype.spread = function (fn) {
  return this.then(function (arr) {
      return fn.apply(this, arr);
  });
};
// end mock

var getFoo = function (fail) {
    var promises = [];
    promises.push(getBar1().then(function (bar1) {
        processBar1(bar1);
        if (fail) {
          throw 'getBar1 Failed!';
        }
        return bar1;
    }));
    promises.push(getBar2().then(function (bar2) {
        processBar2(bar2);
        return bar2;
    }));
    return Promise.all(promises).spread(function (bar1, bar2) {
        var result = processBothBars(bar1, bar2);
        return result;
    });
}
getFoo().then(function (result) {
    console.log(result); // ['bar1', 'bar2']
});
getFoo(true).then(function (result) {
    console.log(result); // doesn't happen
}).catch(function (e) {
    console.error(e); // Error: getBar1 Failed!
});

.then 返回一个 Promise,因此无需创建一个新的包装它的 Promise,除非您想防止错误到达外部 Promise。

【讨论】:

  • 您忘记了 2 个 thens 中的 returning 值
  • 如果您不返回任何内容,您将返回undefined
  • 来自getBar1() 的错误是否会传播到getFoo() 的结果?
  • @Petah:是的 - 与问题中提供的代码相比
  • @Petah 添加了演示,带有(弱)模拟的 .spread,仅适用于具有原生 Promise 支持的浏览器。包括错误示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-21
  • 2020-10-20
  • 2016-04-26
  • 2017-03-17
  • 1970-01-01
  • 1970-01-01
  • 2013-04-09
相关资源
最近更新 更多