Лучший способ обработки вложенных Promises (bluebird)

Ниже приведена следующая цепочка обещаний, и она кажется довольно беспорядочной (каждая функция _create * возвращает обещание):

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

Ответ 1

Я точно знаю, что вы просите, но.

  • Если вы хотите последовательно запустить массив promises

Ответ 2

Лично, когда ситуация становится беспорядочной с зависимостями, я предпочитаю следующий подход:

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

Здесь есть одна тонкая ловушка, о которой вы должны знать. Порядок, в котором определены promises, не обязательно является порядком, в котором вызываются функции. Вот как выглядят зависимости:

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 (хотя его значение не требуется), вы можете заставить его быть последовательным.