2016-09-06 57 views
0

我有一個執行異步操作的順序問題。以下是我的代碼,它向第三方API發送請求以創建發票項目,然後發送另一個創建發票的請求。我的問題是,發票是在調整發票項目創建之前創建的。Node.js - Promise執行順序問題

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

這段代碼有什麼問題?

+0

最明顯的問題是使用的['Promise'構造反模式](http://stackoverflow.com/q/23803743/1048572) – Bergi

回答

3

這是因爲下面的結構組成:

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

你既然從第一回調不return什麼,許鏈不等待任何事情,而在第二個回調立即調用resolve。在createInvoiceItem之後調用resolve來得太遲。

但是你真正的問題是Promise constructor antipattern的普遍使用。您不能在任何地方使用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'); 
    }); 
} 
+0

你有什麼建議,然後?而不是使用Promise? – Shaolin

+0

@Shaolin:什麼都不用 - 所有的功能,包括'then',都已經回覆了諾言!看到我的答案中的代碼。 – Bergi

+0

好的,謝謝你節省了很多時間。我認爲如果價值在內部得到回報,這就夠了 – Shaolin