Best way to handle nested Promises (bluebird)

Conner McNamara picture Conner McNamara · Oct 10, 2014 · Viewed 7.8k times · Source

I have the following promise chain below and it appears to be quite messy (Each _create* function returns a 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);
          });
      });

I know there are Promise functions I can use like all() and join() but these seem to run the functions concurrently which I can't do because persisting to some tables require fields from the previously persisted tables. I'm hoping there is some way for me to do something like the following but I can't seem to find out how:

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

Answer

gman picture gman · Oct 11, 2014

I'm exactly sure what you're asking but.

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