引言
在现代的 JavaScript 开发中,异步编程是一个非常重要的概念。随着前端应用的复杂度不断增加,我们经常需要同时处理多个异步任务。为了更好地管理这些任务,JavaScript 提供了多种 Promise 组合方法,如 Promise.all
、Promise.race
、Promise.allSettled
、 Promise.any
。本文将深入探讨这些方法的使用方法和区别。
Promise
在开始之前,我们先简单回顾一下 Promise 的基本概念。Promise 是 JavaScript 中用于处理异步操作的对象。它代表了一个异步操作的最终完成(或失败)及其结果值。Promise 有三种状态:
- Pending:初始状态,既不是成功,也不是失败。
- Fulfilled:操作成功完成。
- Rejected:操作失败。
并发与并行
并发(Concurrency)
指的是多个任务在同一时间段内交替执行,但不一定是同时执行。在单核 CPU 上,多个任务通过时间片轮转的方式实现并发。Promise.all
是一种伪并发。
在单线程环境中,如 JavaScript 的浏览器运行时或 Node.js 环境中,这些任务并不是同时执行的,而是通过事件循环和异步I/O快速切换执行上下文来实现看起来像是同时发生的效果。使用 Promise.all
可以让多个异步操作并发执行,这意味着它们开始的时间点很接近,并且等待所有操作完成后再继续后续步骤。
并行(Parallelism)
指的是多个任务在同一时刻同时执行。在多核 CPU 上,多个任务可以真正同时执行。这通常需要多核处理器的支持,通过操作系统调度不同的线程或进程在不同的CPU核心上同时执行任务。
JavaScript 本身是单线程的,因此不能直接实现并行执行。然而,在某些特定场景下,比如使用 Web Workers 在浏览器端或者 worker threads 在 Node.js 中,可以创建额外的线程来实现真正的并行处理。
Promise 同时处理多个异步任务
当你需要同时处理多个异步任务时,Promise 给出了几种方法,它们都需要传入一个全为 Promise 的数组作为参数, 本例中我将会使用到的 Promise 数组如下
const weather = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ temp: 29,conditons: 'Sunny with Clouds'})
// reject('error')
},3000)
})
const tweets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('I like cake, BBQ is ready now!')
// reject('BBQ 糊了')
},5000)
})
Promise.all:等待所有任务完成
Promise.all
是 JavaScript 中最常用的 Promise 组合方法之一。它接受一个 Promise 实例数组作为参数,并返回一个新的 Promise。这个新的 Promise 会在所有传入的 Promise 都成功完成时被解决,返回一个包含所有结果的数组。如果其中任何一个 Promise 失败,Promise.all
会立即返回失败的结果。Promise.all
返回的结果数组会按照传入的 Promise 顺序排列,而不是按照完成的顺序。
Promise
.all([weather,tweets]) // 只要有一个错的 就返回错误 全对全返回
.then(responses => {
console.log(responses); // 按实例的顺序 与实例中的setTimeout 无关
})
.catch(err => {
console.log(err);
})
当 Promise 全部成功时
const weather = new Promise((resolve, reject) => {
setTimeout(() => {
reject('error')
},3000)
})
const tweets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('I like cake, BBQ is ready now!')
},5000)
})
其中有 Promise 失败
const weather = new Promise((resolve, reject) => {
setTimeout(() => {
},3000)
})
const tweets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('I like cake, BBQ is ready now!')
},5000)
})
Promise.race:返回第一个完成的任务
Promise.race
是另一个常用的 Promise 组合方法。它同样接受一个 Promise 实例数组作为参数,但不同的是,Promise.race
会返回第一个被解决(无论是成功还是失败)的 Promise 的结果。
Promise.race
通常用于需要快速响应的场景。
Promise
.race([weather,tweets]) // 不考虑成功还是失败 只返回最快的那一个
.then(responses => {
console.log(responses); // 按实例的顺序 与实例中的setTimeout 无关
})
.catch(err => {
console.log(err);
})
最快的那个成功
const weather = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ temp: 29,conditons: 'Sunny with Clouds'})
},3000)
})
const tweets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('I like cake, BBQ is ready now!')
// reject('BBQ 糊了')
},5000)
})
最快的那个失败
const weather = new Promise((resolve, reject) => {
setTimeout(() => {
reject('error')
},3000)
})
const tweets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('I like cake, BBQ is ready now!')
// reject('BBQ 糊了')
},5000)
})
Promise.allSettled:返回所有任务的结果
Promise.allSettled
是 ES2020 引入的新方法。它接受一个 Promise 实例数组作为参数,并返回一个新的 Promise。这个新的 Promise 会在所有传入的 Promise 都完成(无论是成功还是失败)时被解决,返回一个包含所有结果的数组。Promise.allSettled
非常适合用于需要处理多个异步任务,并且希望获取所有任务的结果(无论成功还是失败)的场景。Promise.allSettled
返回的结果数组中的每个元素都是一个对象,包含 status
和 value(成功)
或 reason(失败)
属性。
代码示例
const weather = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve({ temp: 29,conditons: 'Sunny with Clouds'})
reject('error')
},3000)
})
const tweets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('I like cake, BBQ is ready now!')
// reject('BBQ 糊了')
},5000)
})
Promise
.allSettled([weather,tweets]) // 全部执行完毕 不管成功还是失败
.then(responses => {
console.log(responses); // 按实例的顺序 与实例中的setTimeout 无关
})
.catch(err => {
console.log(err);
})
返回全部结果
Promise.any:返回第一个成功的任务
Promise.any
是 ES2021 引入的新方法。它接受一个 Promise 实例数组作为参数,并返回一个新的 Promise。这个新的 Promise 会在第一个成功的 Promise 完成时被解决,返回该 Promise 的结果。如果所有 Promise 都失败,Promise.any
会返回一个 AggregateError
。Promise.any
非常适合用于需要获取第一个成功结果的场景。如果所有 Promise 都失败,Promise.any
会返回一个 AggregateError
,包含所有失败的原因。
代码示例
Promise
.any([weather,tweets]) // 只要有一个成功就返回 只返回一个
.then(responses => {
console.log(responses); // 按实例的顺序 与实例中的setTimeout 无关
})
.catch(err => {
console.log(err);
})
返回第一个成功
const weather = new Promise((resolve, reject) => {
setTimeout(() => {
reject('error')
},3000)
})
const tweets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('I like cake, BBQ is ready now!')
},5000)
})
如果都成功,返回第一个
const weather = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ temp: 29,conditons: 'Sunny with Clouds'})
},3000)
})
const tweets = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('I like cake, BBQ is ready now!')
},5000)
})
如果全都失败,返回一个 AggregateError
,包含所有失败的原因
const weather = new Promise((resolve, reject) => {
setTimeout(() => {
reject('error')
},3000)
})
const tweets = new Promise((resolve, reject) => {
setTimeout(() => {
reject('BBQ 糊了')
},5000)
})
总结
Promise.all
:只要有一个错的 就返回错误 全对全返回。Promise.race
:不考虑成功还是失败 只返回最快的那一个。Promise.allSettled
:全部执行完毕 不管成功还是失败。Promise.any
:只要有一个成功就返回 只返回一个。
在实际开发中,根据具体需求选择合适的 Promise 组合方法,可以大大提高代码的效率和可读性。下一篇文章将会趁热介绍 手写Promsie.all。