В вашем коде нет ничего, следящего за результатом cost().then(...)
, так что часть кода запускается и забывается. То же самое верно для вашего звонка на models.Billing.create
и одного из then
в верхней части вашего кода. Вот почему вы видите результат, который вы есть. При использовании обещаний будьте в поиске мест, где вы создаете обещания и не возвращаете их более высокому абоненту. Это часто предполагает создание обещания, за которым не следят.
Чтобы это исправить:
Прежде всего, исправьте then
в верхней части кода, чтобы результат createObjects
действительно возвращался:
.then( (newData) => {
return createObjects(newData); // this is a async function
})
Еще лучше:
.then(createObjects)
После того, как это исправлено ...
Вариант 1 - использовать reduce
вместо forEach
Используйте этот подход, если хотите убедиться, что запросы выполняются по одному (последовательно), а не по всем сразу:
function processBillGroups(groups) {
return groups.reduce((last, group) => {
var group_id = group.id;
var usage = group.Metric.UsageAmount;
return last
.then(() => calculateCost(usage, group_id))
.then((cost) => models.Billing.create({ group_id, cost, usage }))
}, Promise.resolve());
}
function createObjects(newData) {
return newData.reduce(
(last, { Groups }) => last.then(() => processBillGroups(Groups)),
Promise.resolve(),
);
}
Вариант 1.1 Использование async
/ await
Это также будет выполнять действия последовательно, но вместо манипулирования прямым обещанием используется синтаксис async
/ await
.
async function processBillGroups(groups) {
for (group of groups) {
let group_id = group.id;
let usage = group.Metric.UsageAmount;
let cost = await calculateCost(usage, group_id);
await models.Billing.create({ group_id, cost, usage });
}
}
async function createObjects(newData) {
for ({ Groups } of newData) {
await processBillGroups(Groups);
}
}
Вариант 2 - используйте map
и Promise.all
вместо forEach
Используйте это, если вы не возражаете против всех действий, выполняемых одновременно (параллельно), или даже предпочитаете, чтобы они выполнялись параллельно. createObjects
вернет одно обещание, которое будет выполнено после выполнения всех действий:
function processBillGroups(groups) {
return Promise.all(groups.map((group) => {
var group_id = group.id;
var usage = group.Metric.UsageAmount;
return calculateCost(usage, group_id)
.then((cost) => models.Billing.create({ group_id, cost, usage }));
}));
}
function createObjects(newData) {
return Promise.all(
newData.map(({ Groups }) => processBillGroups(Groups))
);
}
Опция 2.1 - Используйте map
и Promise.all
с небольшим количеством async
/ await
:
Действует так же, как вариант 2, но синтаксис немного лучше.
function processBillGroups(groups) {
return Promise.all(groups.map(async (group) => {
let group_id = group.id;
let usage = group.Metric.UsageAmount;
let cost = await calculateCost(usage, group_id);
await models.Billing.create({ group_id, cost, usage });
}));
}
function createObjects(newData) {
return Promise.all(
newData.map(({ Groups }) => processBillGroups(Groups))
);
}