处理嵌套的 Promises(bluebird)的最佳方法

12

我有下面的promise链,看起来很混乱(每个_create*函数都返回一个promise):

return new Promise(function (resolve, reject) {
      _this.database.transaction(function (t) {
        _this._createExternalAccount(payment, t)
          .then(function (externalAccount) {
            return _this._createExternalTransaction(externalAccount, payment, t)
              .then(function (externalTransaction) {
                return _this._createAddress(externalAccount, payment, t)
                  .then(function (address) {
                    return _this._createTransaction(address, payment, t)
                      .then(function (transaction) {
                        return _this._createGatewayTransaction(externalTransaction, transaction, payment, t)
                          .then(function (gatewayTransaction) {
                            t.commit();
                            resolve(bridgePayment);
                          });
                      });
                  });
              });
          })
          .error(function (bridgePayment) {
            t.rollback();
            reject(bridgePayment);
          });
      });

我知道有Promise函数可以使用,比如all()join(),但是这些似乎是并行运行函数的,而我不能这样做,因为将某些表持久化需要来自先前持久化表的字段。我希望有一种方法让我像下面这样做,但我似乎找不到如何做:

Promise.all(_this._createExternalAccount(payment, t), _this._createExternalTransaction(externalAccount, payment, t), _this._createAddress(externalAccount, payment, t))
    .then(function(externalAccount, externalTransaction, address) {
        // do logic
    });

3
您可以尝试使用链式调用:p.then().then().then().then(),而不是嵌套调用。该方法可使代码更加简洁易懂,同时不改变原有的意义。 - jfriend00
检查 Bluebird 的 .map(),您可以传递 {concurrency: N},以便一次只执行 N 个操作。 - aarosil
2个回答

6
我不确定你在问什么,但是这与IT技术有关。请注意,本文中保留了HTML标签。
  1. If you want to run an array of promises sequentially there's this answer

    The important thing to note is it's not an array of promises. It's an array of functions that make a promise. That's because promises execute immediately so you can't create the promise until you need it.

  2. If you don't want to put them in array though the normal thing is just chain them normally.

    Again the easiest way to to make a bunch functions the return promises. Then you just

    var p = firstFunctionThatReturnsAPromise()
    .then(secondFunctionThatReturnsAPromise)
    .then(thirdFunctionThatReturnsAPromise)
    .then(fourthFunctionThatReturnsAPromise)
    

    You can nest them just as easily

    function AOuterFunctionThatReturnsAPromise() {         
        var p = firstFunctionThatReturnsAPromise()
                .then(secondFunctionThatReturnsAPromise)
                .then(thirdFunctionThatReturnsAPromise)
                .then(fourthFunctionThatReturnsAPromise);
        return p;
    };
    

    Now that outer function is just another function returning a promise which means you can apply same pattern as the inner functions.

    If course those can be inline

    var p = function() {
      return new Promise(resolve, reject) {
        DoSomethingAsync(function(err, result) {
          if (err) {
            reject();
          } else {
            resolve(result);
        };
      };
    }).then(function() {
      return new Promise(resolve, reject) {
        DoSomethingAsync(function(err, result) {
          if (err) {
            reject(err);
          } else {
            resolve(result);
        };
      };
    }).then(function() {
      var err = DoSomethingNotAsync();
      if (err) {
         return Promise.reject(err);
      } else {
         return Promise.resolve();
      }
    });
    

    etc...


3

个人而言,当依赖关系变得混乱时,我更喜欢以下方法:

var externalAccount     = Promise.join(payment, t,                                   createExternalAccount),
    externalTransaction = Promise.join(externalAccount, payment, t,                  createExternalTransaction),
    address             = Promise.join(externalAccount, payment, t,                  createAddress),
    transaction         = Promise.join(address, payment,                             createTransaction),
    gatewayTransaction  = Promise.join(externalTransaction, transaction, payment, t, createGatewayTransaction);

这样做可以使一切更加清晰,但这是一个风格问题。

如果你想要在获取 gatewayTransaction 的值之后执行一些操作(当然是异步的),只需:

gatewayTransaction
    .then(function (val) {
        // do stuff
    })
    .catch(function (err) {
        // do stuff
    });

这里存在一个微妙的陷阱,您需要注意。承诺的定义顺序不一定是函数调用顺序。以下是依赖项的样子:

externalAccount -----> externalTransaction -----> gatewayTransaction
                |--> address --> transaction --|

虽然这对性能来说很好,但有时候您可能希望将整个过程按顺序执行(就像回调金字塔一样)。在这种情况下,您可以编写以下代码:

var externalAccount     = Promise.join(payment, t,                                       createExternalAccount),
    externalTransaction = Promise.join(externalAccount, payment, t,                      createExternalTransaction),
    address             = Promise.join(externalAccount, payment, t, externalTransaction, createAddress),
    transaction         = Promise.join(address, payment,                                 createTransaction),
    gatewayTransaction  = Promise.join(externalTransaction, transaction, payment, t,     createGatewayTransaction);

通过将externalTransaction添加到address的依赖项中(即使不需要其值),您可以强制该过程按顺序执行。

2
这绝对是我与Gorgi和Petka讨论将join添加到Bluebird时所考虑的。我认为这是一种不错的风格。 - Benjamin Gruenbaum
很高兴听到,我真的很喜欢它。 - ldmat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接