map() with async vs promise.all()



如果我有一个元素数组,并且我想对它们进行并行操作

我会使用promise.all()

我知道promise.all()接受一系列承诺。如果我错了,请纠正我,我不这么认为。

这里,它清楚地说。

Promise.all((方法返回一个Promise,当作为可迭代对象传递的所有Promise都已实现时,或者当可迭代对象不包含Promise时,或者在可迭代对象包含已实现的Promise和已返回的非Promise时实现该Promise。它以拒绝的第一个promise的原因进行拒绝,或者如果第一个参数使用try/catch/sthrow块捕获了内部的错误,则以第一个参数捕获的错误进行拒绝。

所以,是的,我们可以将简单的函数传递给promise.all(),如果它们返回,它就会解析,如果它们抛出错误,它就会拒绝

现在看下面的代码。

const promises = todayAssignedJobs.map(async todayAssigned => {
const [leaderboard, created] = await Leaderboard.findOrCreate({...});
if (!created) {
const rating = (todayAssigned.rating + leaderboard.rating * leaderboard.jobs_completed) / (leaderboard.jobs_completed + 1);
const commission = todayAssigned.commission + leaderboard.commission;
const jobsCompleted = leaderboard.jobs_completed + 1;
await Leaderboard.update({
rating,
commission,
jobs_completed: jobsCompleted,
updated_by: 'system',
}, {
where: {
id: leaderboard.id,
},
});
}
await AssignedJob.update({
is_leaderboard_generated: true,
}, {
where: {
id: todayAssigned.id,
},
});
});
await Promise.all(promises);

在这里,我有一个疑问

我们对数组的每个元素进行迭代,并异步地对它们进行操作。他们不会明确返回任何内容

所以,我认为map在这里也在做并行操作

为什么在这里使用promise.all()

.map()不支持promise。因此,当您像现在这样向它传递async回调时,它不会理会返回的promise。因此,它只是一个接一个地运行循环,而不等待任何返回的承诺。因此,在.map()循环中启动的所有异步操作将同时进行。

如果这就是你想要的,并且你想要收集所有返回的承诺,以便稍后可以看到它们何时都用Promise.all()完成,那么这个模式工作得很好:

Promise.all(someArray.map(callbackThatReturnsAPromiseHere))

事实上,Bluebird promise库有一个特殊的函数,它将这两者结合在一起,称为Promise.map()。它还提供了另一个很好的功能,可以控制一次可以运行多少并发异步操作(因为它的map()操作是promise感知的(。

听起来,您正试图弄清楚是否应该只使用.map()而不使用Promise.all()。如果这样做,您将并行运行异步操作,但您不知道这些操作何时全部完成,也不知道收集所有结果的能力。您可以在返回的promise数组中使用Promise.all()来了解它们何时全部完成和/或收集它们已解析的结果。

仅供参考,.map()只是一个简单的循环。它没有任何特殊的异步功能或任何特殊的并行运行功能。如果您愿意,也可以使用for循环执行同样的操作。它不会暂停async回调以等待它完成,因此运行它的副作用是启动一堆并行异步操作。

如果您想在所有操作完成后执行某些操作,则只需要Promise.all。例如:

const promises = todayAssignedJobs.map(async todayAssigned => {
// lots of async stuff
});
await Promise.all(promises);
// now, all Promises have resolved
// alert the user that the leaderboard is completely updated

如果在确定所有Promises都已完成后,您不需要发生任何事情,那么Promise.all就没有意义了——您还可以在循环中创建Promises并保持原样。在这种情况下,由于您不会使用Promises的结果数组,因此更适合使用forEach(这是用于副作用的数组方法(。

不过,有一个问题-你没有处理错误,但错误应该被处理,否则他们会发出警告或退出节点进程:

const processJob = async (todayAssigned) => {
const [leaderboard, created] = await Leaderboard.findOrCreate({...});
if (!created) {
// ...
// ...
todayAssignedJobs.forEach(async todayAssigned => {
try {
await processJob(todayAssigned);
} catch(e) {
// handle errors
}
});

这里Promise.all的目的是能够在继续之前等待所有承诺。

如果您在await Promise.all(promises);之前添加了console.log,那么它将在任何promise解析之前同步运行,而紧接在该行之后的console.log将仅在promise解析后出现。

最新更新