关于 javascript: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(),如果它们返回则解析,如果抛出错误则拒绝。
现在看看下面的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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() 呢?

  • 为什么要在这里使用 promise.all() 呢?相对于..?您是否正在考虑使用 for 循环?
  • 一点也不,如果 map() 异步执行。为什么我们两者都需要? map() 是否在这里等待第一个异步操作完成?在这种情况下,promise.all() 做了什么?
  • “他们没有明确返回任何东西”?? async 函数根据定义返回一个 Promise
  • 在所有映射的异步函数完成后,您是否需要执行任何操作?如果是这样,这就是您需要 Promise.all() 的目的。如果没有,那么你可以不用它,你的映射函数将做他们必须做的事情,而不会中断外面的代码
  • “如果 map() 异步执行” map 不会异步执行任何操作。
  • @Kaiido 我们正在对每个元素进行异步操作。它可能会等待完成(请在此处纠正我),但操作在这里是异步的。
  • “它可能会等待它完成”??如果”它”是指 .map(),那么不,它不会等待任何东西
  • @Phil如果它不等待,那么它正在并行地做事。对?
  • 正确,但下面有更好的信息??
  • 不,地图都是同步的。它会同步声明你的 Promise。使用 setTimeout 可能更容易掌握? [1,2,3,4].map( () => setTimeout( doSomething, 3000) ) 将同步返回 setTimeout() 的结果(超时 id),即使 doSomething 只会在 3 秒内被调用
  • @Phil 我在这里缺少的一条非常有用的信息是这个 async fun() { await some(); await another(); } 将在这里返回什么?
  • @SujeetAgrahari 一个 Promise 在 some() 和 another() 完成时解析(没有值)
  • @Phil 知道了,fun() 将返回一个 Promise。当所有Promise都解决时,它将被解决,如果其中一个被拒绝,它将被拒绝?
  • @SujeetAgrahari 正确。相当于 return some().then(() => another()).then(() => {})


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

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

1
Promise.all(someArray.map(callbackThatReturnsAPromiseHere))

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

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

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

  • 我大吃一惊。至少我是对的,map 会并行触发它们。它不会等待他们的返回值,这一行很棒。我不知道。这就是为什么我应该做 promise.all() 以确保所有Promise都得到履行。 :)
  • 所以,基本上现在 promise.all() 将观察他们返回的Promise,如果他们得到解决,对吧?
  • @SujeetAgrahari – 是的, promise.all() 的全部目的是观察一系列Promise,并告诉您第一个Promise何时拒绝(并向您报告拒绝原因)或它们何时全部完成(并报告一系列已解决的值) 并且 .map() 不会为您执行此操作。 .map() 将为您收集 Promise 数组,就像它从回调中收集任何返回值并运行初始循环一样,但这就是它所做的一切。


如果你想在所有操作完成后做某事,你只需要Promise.all。例如:

1
2
3
4
5
6
7
8
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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 都解决之后才会出现。


来源:https://www.codenong.com/61379705/

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?