开发者问题收集

Node.js - Promise 执行顺序问题

2016-09-06
214

我在执行异步操作的顺序方面遇到问题。下面是我的代码,它向第三方 API 发送一个请求以创建发票项目,然后发送另一个请求以创建发票。我的问题是,发票是在创建调整发票项目之前创建的。

function createInvoiceItems(invoice, customerDetails) {
  return new Promise((resolve, reject) => {
    const invoiceItems = invoice.invoiceItems;
    const invoiceCurrency = invoice.currency;

    const promises = invoiceItems.map((invoiceItem) =>
      new Promise((fulfill, deny) => {
        invoiceItem.currency = invoiceCurrency;

        createInvoiceItem(customerDetails, invoiceItem)
          .then((item) => fulfill(invoiceItem.amount))
          .catch((error) => deny(error));
      })
    );

    return Promise.all(promises)
      .then((lineTotals) => {
        if (lineTotals.length > 0) {
          const invoiceTotal = calculateInvoiceTotal(lineTotals);
          console.log('INVOICE TOTALS ######################', invoiceTotal);
          const customerCommitment = invoice.commitment;

          //Create adjustment invoice item
          if (invoiceTotal < customerCommitment) {
            const adjustmentAmount = (customerCommitment - invoiceTotal);
            console.log('ADJUSTMENT AMOUNT ######################', adjustmentAmount);
            const invoiceItem = {
              currency: invoiceCurrency,
              amount: adjustmentAmount,
              description: 'Adjustment',
            };

            createInvoiceItem(customerDetails, invoiceItem)
              .then((item) => {
                if (typeof item === 'object') {
                  return resolve(customerDetails.stripeId);
                }
              })
              .catch((error) => reject(error));
          }
        }
      })
      .then(() => resolve(customerDetails.stripeId))
      .catch((error) => reject(error));
  });
}

/**
 * Generates customer invoices
 * @param invoices
 */
function generateInvoices(invoices) {
  return new Promise((resolveInvoice, rejectInvoice) => {
    const promises = invoices.map((invoice) =>
      new Promise((resolve, reject) => {
        let invoiceDetails;

        getInvoice(invoice)
          .then((invoiceObject) => {
            invoiceDetails = invoiceObject;
            return getCustomerDetails(invoiceDetails.customerId);
          })
          .then((customerDetails) => createInvoiceItems(invoiceDetails, customerDetails))
          .then((customerId) => createInvoice(customerId))
          .then((thirdPartyInvoice) => {
            invoiceDetails.close = true;

            updateInvoiceFile(invoiceDetails)
              .then(() => resolve(thirdPartyInvoice.id));
          })
          .catch((error) => reject(error));
      })
    );

    Promise.all(promises)
      .then((invoice) => {
        if (invoice.length > 0) {
          return resolveInvoice(invoice);
        }
        return rejectInvoice('Invoice could not be generated');
      })
      .catch((error) => rejectInvoice(error));
  });
}

此代码有什么问题?

1个回答

发生这种情况是因为以下构造:

.then(… => {
    …
    createInvoiceItem(customerDetails, invoiceItem)
    .then(… => {
        resolve(customerDetails.stripeId);
    })
})
.then(() => resolve(…))

由于您没有从第一个回调中 返回 任何内容,因此承诺链不会等待任何内容,并在第二个回调中立即调用 resolve 。在 createInvoiceItem 之后调用 resolve 来得太晚了。

但您的真正问题是 Promise 构造函数反模式 的普遍使用。您不得在任何地方使用 Promise 构造函数 - then 链!这大大简化了您的代码:

function createInvoiceItems(invoice, customerDetails) {
  const invoiceItems = invoice.invoiceItems;
  const invoiceCurrency = invoice.currency;

  const promises = invoiceItems.map(invoiceItem => {
    invoiceItem.currency = invoiceCurrency;
    return createInvoiceItem(customerDetails, invoiceItem)
      .then(item => invoiceItem.amount);
  });

  return Promise.all(promises)
    .then(lineTotals => {
      if (lineTotals.length > 0) {
        const invoiceTotal = calculateInvoiceTotal(lineTotals);
        console.log('INVOICE TOTALS ######################', invoiceTotal);
        const customerCommitment = invoice.commitment;

        //Create adjustment invoice item
        if (invoiceTotal < customerCommitment) {
          const adjustmentAmount = (customerCommitment - invoiceTotal);
          console.log('ADJUSTMENT AMOUNT ######################', adjustmentAmount);
          const invoiceItem = {
            currency: invoiceCurrency,
            amount: adjustmentAmount,
            description: 'Adjustment',
          };

          return createInvoiceItem(customerDetails, invoiceItem)
            .then(item => {
              if (typeof item === 'object') {
                return customerDetails.stripeId;
              }
              // not sure what should happen otherwise?
            });
        }
      } // else
      return customerDetails.stripeId;
    });
}

function generateInvoices(invoices) {
  const promises = invoices.map(invoice =>
    getInvoice(invoice)
    .then(invoiceObject =>
      getCustomerDetails(invoiceDetails.customerId);
      .then(customerDetails => createInvoiceItems(invoiceDetails, customerDetails))
      .then(customerId => createInvoice(customerId))
      .then(thirdPartyInvoice => {
        invoiceObject.close = true;
        return updateInvoiceFile(invoiceDetails)
        .then(() => thirdPartyInvoice.id);
      })
    )
  );

  return Promise.all(promises)
  .then(invoice => {
    if (invoice.length > 0)
      return invoice;
    else
      throw new Error('Invoice could not be generated');
  });
}
Bergi
2016-09-06