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