【问题标题】:Which is better Promise.all or nested async await?Promise.all 和嵌套异步等待哪个更好?
【发布时间】:2019-11-27 02:29:56
【问题描述】:

我有两个代码块。首先是使用async await

  async sendEmailNotifications() {
    try {
      const users = await User.find(...)

      const promises = users.map(async(user) => {
        const _promises = user.appId.map(async(app) => {
            const todayVisitorsCount = await Session.count({...})
            const yesterdayVisitorsCount = await UserSession.count({...})
            const emailObj = {
              todayVisitorsCount,
              yesterdayVisitorsCount
            }
            const sendNotification = await emailService.analyticsNotification(emailObj)
        })
        await Promise.all(_promises)
      })
      return promises
    } catch (err) {
      return err
    }
  }
(await sendEmailNotifications())

然后我使用Promise.all

    sendEmailNotifications() {
      const users = await User.find(...)
      const promises = users.map((user) => {
        const allPromises = []
        user.appId.map((app) => {
          allPromises.push(UserSession.count({...}))
          allPromises.push(Session.count({...}))
        })
        const data = await Promise.all(allPromises)
        const emailObj = {
          todayVisitorsCount: data[0],
          yesterdayVisitorsCount: data[1]
        }
        const sendNotification = await emailService.analyticsNotification(emailObj)
      })
      return promises
  }


  sendNotification.then((data) => console.log(data))

现在我需要知道哪段代码执行得更快?一个是系列(异步等待),一个是并行(Promise.all)。哪个性能更好?

【问题讨论】:

  • 嵌套异步等待将序列化您的调用,而 promise.all 将使它们并行化。所以这取决于你需要什么。
  • 看看这个 - hackernoon.com/…
  • 在您的第一个 sn-p 中,您映射到 user.appId。那个循环完全从你的第二个 sn-p 中消失了,为什么?他们现在几乎没有可比性。
  • 在您的第一个 sn-p 中,您返回了一系列承诺,但没有等待它们。您需要再次在它们上使用Promise.allreturn Promise.all(promises)。我也怀疑你是否想返回一个错误对象——如果你不能处理一个异常,就让它冒泡。
  • @Bergi 你正在映射 user.appId 在这两个 sn-p 中我都在循环 user.appId(请看)。 在你的第一次......时间:return Promise.all(promises) 不,我正在返回 promises 并使用 await (await sendEmailNotifications())如果你无法处理异常我会处理它(问题是出于不同目的)

标签: javascript node.js asynchronous promise async-await


【解决方案1】:

在第一个代码中,您有两个单独的 await 语句:

        const todayVisitorsCount = await Session.count({...})
        const yesterdayVisitorsCount = await UserSession.count({...})

而在第二个中,您只有一个,在 Promise.all 之前:

const data = await Promise.all(allPromises)

在第一个代码中,第二个 Promise 只会在第一个 Promise 完成后初始化,导致脚本结束需要更长的时间。例如:

const fn = () => new Promise(resolve => setTimeout(resolve, 1000));

console.log('start');
(async () => {
  await fn();
  await fn();
  console.log('two awaits done');
})();

(async () => {
  await Promise.all([fn(), fn()]);
  console.log('Promise.all done');
})();

没有Promise.all 的版本在第一次调用fn() 时会暂停函数,并等待fn() 返回的Promise 解决(1000 毫秒),然后再继续下一行。下一行再次调用fn()await 等待它完成(再过 1000 毫秒)。

相比之下,Promise.all 版本会立即调用两个fn()s - 两个 Promise 都已初始化,而暂停函数的 await 正在等待 两个 Promise 完成。第一个 Promise 的初始化和第二个 Promise 的初始化之间没有停机时间。

因此,Promise.all 版本的运行速度将明显快于具有两个 awaits 的版本。最好使用Promise.all除非第一个 Promise (UserSession.count)必须在第二个 Promise (Session.count) 开始之前完成。

通过解构并且没有不必要的变量,这就是我清理您的Promise.all 代码的方式,您可能会认为它更具可读性:

async sendEmailNotifications() {
  const users = await User.find();
  return users.map(async (user) => {
    const [todayVisitorsCount, yesterdayVisitorsCount] = await Promise.all([
      UserSession.count(),
      Session.count()
    ]);
    await emailService.analyticsNotification({ todayVisitorsCount, yesterdayVisitorsCount });
  });
}

【讨论】:

  • 另一种(诚然不太可能)的情况是,如果Session.countUserSession.count 由于某种奇怪的原因以一种糟糕的方式交互并且如果一起运行需要更长的时间(可能是奇怪的数据库争用?)所以串行会最终比并行更快......正如我所说,不太可能但在某些情况下值得牢记。
  • 它不是“初始化承诺”,而是它开始操作。这就是重点。 await Session.count({}) 后跟 await User.count({}) 表示在 Session.count({}) 的承诺履行之前不会调用 User.count({}),因此它在 Session.count({}) 的承诺履行之前不会开始操作。如果操作应该并行完成,重要的是先启动它们,然后等待它们的结果。
  • @Profer await 不是 Promise.all 的替代品 - 他们做了两件不同的事情。 awaitPromise.prototype.then 的替代品(大部分情况下),主要是为了让异步代码看起来更扁平、更易读。看看这个页面上所有的 sn-ps 是如何使用await - 它有用的,不管你是否使用Promise.allPromise.all 返回一个 Promise,当传递的 Promise 数组中的每一个都解析时解析,这在某些情况下也很有用,包括这种情况。
  • @Profer - 当您想一件一件地做事时,async/await 更容易遵循。每当您想要并行处理时,只需使用“CertainPerformance”所示的Promise.all(您可以将其与await 一起使用,以等待所有这些都完成后再继续)。
  • @T.J.Crowder 确定性能我现在知道了。 async await.then 语法的替代品,而不是 Promise.all。如果我在UserSession.count({...}) 查询中使用了todayVisitorsCount 值,这里async await 会很有用!!!非常感谢你们俩。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-07
  • 2018-10-01
  • 2018-06-25
  • 2020-10-17
  • 2018-06-04
相关资源
最近更新 更多