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 解决就可以开始下一个 promise 时,这种方法很有用。
但它真的是并行运行(或一次性全部运行)吗?
答案是否定的。JavaScript 是单线程编程语言,所以它不能同时运行多个任务(除了某些情况,如 web workers)。 Promise.all()
实际上是并发运行它们,而不是并行!
有什么区别?
并发编程 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()])
// 或
await Promise.all(
items.map(async (item) => {
await doSomething(item)
})
)
或者顺序运行:
for (let item of items) {
await doSomething(item)
}
参考资料
祝你 promise 愉快!