我有一个异步函数(createObjects),该函数需要在数据库中创建一些模型,因此,在创建所有对象之前(在forEach循环内),该函数需要等待。创建最后一个模型后,它应该返回“数据已同步!”。字符串,但“数据已同步!”消息永远不会等待createObjects()完成。我认为我需要将所有model.create promises都返回给mainPromise,就像一个promises数组一样,但是我不知道这样做的最好方法。有什么建议吗?
PS:在createObjects内部调用的calculateCost是异步的,并且运行良好。
mainPromise()
.then( (data) => {
return proccessData(data); //this is a sync function
})
.then( (newData) => {
createObjects(newData); // this is a async function
})
.then( () => {
return "Data Synchronized!";
})
//this needs to be an async function
function createObjects(newData){
newData.forEach((bills) => {
//if the object bills has the Groups array attributes...
if (bills.Groups.length !== 0) {
//iterate through groups
bills.Groups.forEach( (group) => {
var uid = group.id;
var usage = group.Metric.UsageAmount;
var cost = calculateCost(usage, uid); //async function
//the problem is here
cost.then((result) => {
models.Billing.create({
group_id: uid,
cost: result,
usage: usage
});
});
})
}
});
}
var calculateCost = (usage, uid) => {
var cost;
return models.ObjectA.findOne({
where: { id: uid }
}).then((data) => {
if (data.type == "Interactive") {
cost = usage * 0.44;
} else if (data.type == "Automated") {
cost = usage * 0.11;
}
return cost;
});
}
答案 0 :(得分:1)
您的代码中没有任何内容可以监视cost().then(...)
的结果,因此一点点代码都是一劳永逸的。调用models.Billing.create
和代码顶部的then
之一也是如此。这就是为什么您看到自己的结果。使用Promises时,请留意要创建诺言的地方,而不要将它们退还给更高的呼叫者。这通常表明创建了未被遵守的承诺。
要解决此问题:
首先,将then
固定在代码顶部,以便实际上返回createObjects
的结果:
.then( (newData) => {
return createObjects(newData); // this is a async function
})
更好:
.then(createObjects)
在纠正之后...
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(),
);
}
async
/ await
这也将按顺序执行操作,但是使用async
/ await
语法而不是直接的Promise操作。
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);
}
}
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))
);
}
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))
);
}