JavaScript Promise.all() 是并行还是顺序执行的?

- Published on
- /4 mins read/---
假设你有一个异步任务列表(每个任务都返回一个 Promise)。
let promise1 = async function () {
/* ... */
}
let promise2 = async function () {
/* ... */
}
let promise3 = async function () {
/* ... */
}
你会选择如何运行它们?
一个接一个地等待每个 promise:
await promise1()
await promise2()
await promise3()
// 执行其他操作
或者一次性运行所有的 promise:
await Promise.all([promise1(), promise2(), promise3()])
// 执行其他操作
第一种方法是顺序执行,一个接一个地运行。这意味着下一个 promise 只有在前一个 promise 完成后才会开始执行。
就像这样:
promise1().then(() => {
promise2().then(() => {
promise3().then(() => {
// 执行其他操作
})
})
})
第二种方法常被称为**"并行"**执行,意味着所有的 promise 会同时开始执行。 当你不需要等待前一个 promise 完成就可以开始执行下一个时,这种方法就很有用。
但它真的是并行(或同时)执行的吗?
答案是否定的。JavaScript 是单线程编程语言,所以它不能同时执行多个任务(除了一些特殊情况,比如使用 web workers)。 Promise.all()
实际上是并发执行这些 promise,而不是并行执行!
那么,这两者有什么区别呢?
并发编程 vs 并行编程
简单来说:并发编程是同时处理多件事情,而并行编程是同时执行多件事情。
你也可以参考 Haskell wiki 上的这个精彩解释。
给 9 岁小朋友的简单例子:
- 并发:2 队顾客在一个收银员那里轮流点餐(两队交替点餐)。
- 并行:2 队顾客在 2 个收银员那里同时点餐。
因此,Promise.all()
的工作原理是,它将所有 promise 添加到事件循环队列中并一起调用它们。 但它会等待每个 promise 解决后才继续。 如果第一个 promise 被拒绝,Promise.all
就会停止,除非你自己处理错误(比如用 .catch()
)。
这就是并发和并行的主要区别。在并发执行中,promise 一个接一个运行,但不必等待前面的完成。它们同时取得进展。 相比之下,并行执行在独立的进程中同时运行 promise。 这使得它们能够完全独立地按照各自的速度推进。
总结
标题问题的答案是:Promise.all()
是并发执行的,所有的 promise 几乎同时执行,但不是并行执行。
如果你有一组互不依赖的 promise,你可以选择并发(或类并行)执行它们:
await Promise.all([promise1(), promise2(), promise3()])
// or
await Promise.all(
items.map(async (item) => {
await doSomething(item)
})
)
或者顺序执行:
for (let item of items) {
await doSomething(item)
}
参考资料
Happy promise-ing!