【问题标题】:What's the best way to handle a list of promises in an isolated manner?以隔离方式处理承诺列表的最佳方法是什么?
【发布时间】:2022-01-27 04:46:48
【问题描述】:

我一直在思考什么是使用 Javascript 处理大量繁重操作的最佳方法,我想出了以下方法:

const results: Promise<void>[] = [];

// Start all pieces of work concurrently
for (const record of await getData()) {
  results.push(doSomeHeavyWork(records));
}

// Collect the results
for (const result of results) {
  // Isolate the failures so one doesn't break another.
  try {
    await result;
  } catch (error) {
    console.log(JSON.stringify(error));
  }
}

我的理解是,上面的 sn-p 与最长的操作一样长,这与获得 AFAIK 一样好。但是,有没有更好或更惯用的方法来解决这个问题?

我并不一定要在这里查看节点。这可能是节点、Deno 或浏览器代码。

【问题讨论】:

  • 在浏览器代码中,您可以使用setTimeout(myTimeConsumingFunction, 0),它将在单独的浏览器线程中运行该函数。
  • 事件循环不会使任何东西并发。 doSomeHeavyWork 是做什么的?真的是异步工作吗?
  • 假设doSomeHeavyWork 返回一个有时会被拒绝的promise,那么这段代码实际上已经被破坏了,因为它可能导致未处理的promise 拒绝使你的进程崩溃。请参阅 herethere。请改用Promise.all
  • doSomeHeavyWork 做了大量的 IO 吗?如果是这样,使用承诺是有意义的。如果您实际上正在做大量的 CPU 工作,那么您正在做的事情将无济于事,因为一切仍在同一线程中运行。相反,您需要考虑使用工作线程。
  • @JavierGarcíaManzano Doing Promise.all(data.map(doSomeHeavyWork)).catch(…) 等待所有结果,但如果任何一个失败,则完全失败。如果您将.catch() 单独放在每个doSomeHeavyWork 调用上,它也会等待所有结果,但对于那些失败的结果会导致undefined(或您返回的任何catch 回调)。 Promise.allSettled 也将等待所有结果,但让您仅在一切完成后处理(或记录)错误,而不是在错误发生时立即处理。

标签: javascript node.js typescript event-loop deno


【解决方案1】:

在您的代码中,仅将 Promise 推送到数组中不会启动并发工作,但是您将解决它们的方式可以以并发方式进行。在这个for循环中,results数组的每一项都会一个接一个地同步执行,没有任何并发​​执行,性能会非常低。

javascript提供了一种实现这种“并发”执行的方法,使用Promise原生类的all静态方法:

const results: Promise<void>[] = [];

// Start all pieces of work concurrently
for (const record of await getData()) {
  results.push(doSomeHeavyWork(records));
}

// Without handling errors will be just like this
await Promise.all(results);

在上面的例子中,所有的 Promise 都会被解决,但如果一个失败,所有的 Promise 也会失败(如果这不是你想要的,请考虑查看更多关于 allSettled 的信息)。要处理每个 promise 错误,您只需将 catch 块附加到数组中的每个 promise:

const results: Promise<void>[] = [];

// Start all pieces of work concurrently
for (const record of await getData()) {
  results.push(doSomeHeavyWork(records));
}

// Handling errors will be just like this
await Promise.all(results.map(r => r.catch(error => console.log(JSON.stringify(error)))));

注意:并发被低估了,因为 nodeJS 是单线程语言,所以这种并发不会发生在不同的线程中。

【讨论】:

  • "在这个for循环中,results数组的每一项都会一个接一个地同步执行" - 不。正如你所提到的,results 持有承诺,所以当你 await 他们时,没有任何东西“执行”。
  • 我的意思是第二个for 循环,他正在“等待”resultresults 数组的项)。我可能用了错误的词来解释,但是每个承诺都会被等待,当这个问题已经解决或被拒绝时,循环只会转到下一个承诺。 @Bergi
  • 是的,但是没有减速,“性能会非常低”是错误的。所有doSomeHeavyWork 的执行都在第一个循环中开始,Promise.all(settled) 和原始代码之间没有性能差异。只是一个正确性 - Promise.all 不会导致未处理的拒绝崩溃(而且它也是更简单的代码)。
猜你喜欢
  • 2017-12-26
  • 1970-01-01
  • 2018-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
  • 1970-01-01
  • 2014-12-05
相关资源
最近更新 更多