map() 与 async 与 promise.all()

map() with async vs promise.all()

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

我会使用 promise.all()

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

在这里,它清楚地表明。

The Promise.all() method returns a single Promise that fulfills when all of the promises passed as an iterable have been fulfilled or when the iterable contains no promises or when the iterable contains promises that have been fulfilled and non-promises that have been returned. It rejects with the reason of the first promise that rejects, or with the error caught by the first argument if that argument has caught an error inside it using try/catch/throw blocks.

所以,是的,我们可以将简单的函数传递给 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);
Promise.all(someArray.map(callbackThatReturnsAPromiseHere))

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

const processJob = async (todayAssigned) => {

 const [leaderboard, created] = await Leaderboard.findOrCreate({...});



 if (!created) {

  // ...



// ...

todayAssignedJobs.forEach(async todayAssigned => {

 try {

  await processJob(todayAssigned);

 } catch(e) {

  // handle errors

 }

});

在这里,我有一个疑问。

我们正在迭代数组的每个元素并对它们进行异步操作。他们没有明确返回任何东西。

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

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


.map() 不是Promise感知的。所以,当你像你一样向它传递一个 async 回调时,它不会注意返回的Promise。因此,它只是一个接一个地运行循环,而不是等待任何返回的Promise。因此,在 .map() 循环中启动的所有异步操作将同时进行。

如果那是你想要的,并且你想收集所有返回的 Promise,以便以后可以看到它们何时都用 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);
Promise.all(someArray.map(callbackThatReturnsAPromiseHere))

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

const processJob = async (todayAssigned) => {

 const [leaderboard, created] = await Leaderboard.findOrCreate({...});



 if (!created) {

  // ...



// ...

todayAssignedJobs.forEach(async todayAssigned => {

 try {

  await processJob(todayAssigned);

 } catch(e) {

  // handle errors

 }

});

而且,这是一种常见的设计模式。事实上,Bluebird Promise 库有一个特殊的函数将这两者结合起来,称为 Promise.map()。它还提供了另一个不错的功能,让您可以控制一次可以运行多少并发异步操作(因为它的 map() 操作是 promise-aware)。

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

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


如果你想在所有操作完成后做某事,你只需要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);
Promise.all(someArray.map(callbackThatReturnsAPromiseHere))

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

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 都已完成后不需要发生任何事情,那么 Promise.all 就没有意义了 - 您也可以在循环中创建 Promise 并让他们保持原样。在这种情况下,由于您不会使用 Promise 的结果数组,因此使用 forEach (这是用于副作用的数组方法)会更合适。

不过有一个问题 - 你没有处理错误,但应该处理错误,否则它们会给出警告或退出 Node 进程:

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);
Promise.all(someArray.map(callbackThatReturnsAPromiseHere))

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

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 的目的是能够在继续之前等待所有的Promise。

如果你在 await Promise.all(promises); 之前添加了一个console.log,它会在任何promise 解决之前同步运行,而console.log 紧随该行之后只会在所有promise 都解决之后才会出现。


相关推荐

  • Spring部署设置openshift

    Springdeploymentsettingsopenshift我有一个问题让我抓狂了三天。我根据OpenShift帐户上的教程部署了spring-eap6-quickstart代码。我已配置调试选项,并且已将Eclipse工作区与OpehShift服务器同步-服务器上的一切工作正常,但在Eclipse中出现无法消除的错误。我有这个错误:cvc-complex-type.2.4.a:Invali…
    2025-04-161
  • 检查Java中正则表达式中模式的第n次出现

    CheckfornthoccurrenceofpatterninregularexpressioninJava本问题已经有最佳答案,请猛点这里访问。我想使用Java正则表达式检查输入字符串中特定模式的第n次出现。你能建议怎么做吗?这应该可以工作:MatchResultfindNthOccurance(intn,Patternp,CharSequencesrc){Matcherm=p.matcher…
    2025-04-161
  • 如何让 JTable 停留在已编辑的单元格上

    HowtohaveJTablestayingontheeditedcell如果有人编辑JTable的单元格内容并按Enter,则内容会被修改并且表格选择会移动到下一行。是否可以禁止JTable在单元格编辑后转到下一行?原因是我的程序使用ListSelectionListener在单元格选择上同步了其他一些小部件,并且我不想在编辑当前单元格后选择下一行。Enter的默认绑定是名为selectNext…
    2025-04-161
  • Weblogic 12c 部署

    Weblogic12cdeploy我正在尝试将我的应用程序从Tomcat迁移到Weblogic12.2.1.3.0。我能够毫无错误地部署应用程序,但我遇到了与持久性提供程序相关的运行时错误。这是堆栈跟踪:javax.validation.ValidationException:CalltoTraversableResolver.isReachable()threwanexceptionatorg.…
    2025-04-161
  • Resteasy Content-Type 默认值

    ResteasyContent-Typedefaults我正在使用Resteasy编写一个可以返回JSON和XML的应用程序,但可以选择默认为XML。这是我的方法:@GET@Path("/content")@Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})publicStringcontentListRequestXm…
    2025-04-161
  • 代码不会停止运行,在 Java 中

    thecodedoesn'tstoprunning,inJava我正在用Java解决项目Euler中的问题10,即"Thesumoftheprimesbelow10is2+3+5+7=17.Findthesumofalltheprimesbelowtwomillion."我的代码是packageprojecteuler_1;importjava.math.BigInteger;importjava…
    2025-04-161
  • Out of memory java heap space

    Outofmemoryjavaheapspace我正在尝试将大量文件从服务器发送到多个客户端。当我尝试发送大小为700mb的文件时,它显示了"OutOfMemoryjavaheapspace"错误。我正在使用Netbeans7.1.2版本。我还在属性中尝试了VMoption。但仍然发生同样的错误。我认为阅读整个文件存在一些问题。下面的代码最多可用于300mb。请给我一些建议。提前致谢publicc…
    2025-04-161
  • Log4j 记录到共享日志文件

    Log4jLoggingtoaSharedLogFile有没有办法将log4j日志记录事件写入也被其他应用程序写入的日志文件。其他应用程序可以是非Java应用程序。有什么缺点?锁定问题?格式化?Log4j有一个SocketAppender,它将向服务发送事件,您可以自己实现或使用与Log4j捆绑的简单实现。它还支持syslogd和Windows事件日志,这对于尝试将日志输出与来自非Java应用程序…
    2025-04-161