在现代JavaScript开发中,异步编程已经成为必不可少的一部分。为了处理异步操作,JavaScript 提供了多种工具和方法,其中最重要的一种就是 Promise。并且 Promise 中的 Promise.all ,这是一个强大的工具,可以帮助开发者有效地管理多个并发异步操作。
什么是 Promise.all?
Promise.all
是 JavaScript 中的一个静态方法,它接收一个可迭代对象(如数组)作为参数,这个参数包含多个 Promise 实例。Promise.all
返回一个新的 Promise,该 Promise 只有在所有传入的 Promise 都已解决或其中一个 Promise 被拒绝时才会结束。
语法
Promise.all(iterable);
iterable
是一个可迭代对象,包含多个 Promise 或其他值(这些值会被隐式地转换成 resolved 状态的 Promise)。
返回值是一个新的 Promise 实例。
使用示例
让我们通过一个简单的例子来理解 Promise.all
的工作原理。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values); // [3, 42, "foo"]
});
在上面的示例中,Promise.all
接收一个包含三个 Promise 的数组。它返回一个新的 Promise,当这三个 Promise 都被解决时,它会被解决,并且其值是一个包含这三个 Promise 的解决值的数组。
错误处理
如果传入的 Promise 中有一个被拒绝,Promise.all
返回的 Promise 也会被拒绝,并且拒绝的原因是第一个被拒绝的 Promise 的原因。
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => {
setTimeout(reject, 100, 'error occurred');
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 200, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
}).catch((error) => {
console.log(error); // "error occurred"
});
在这个示例中,第二个 Promise 被拒绝,因此 Promise.all
返回的 Promise 也会立即被拒绝,且拒绝的原因是第二个 Promise 的拒绝原因。
实际应用场景
1. 并行执行多个异步操作
Promise.all
可以用来并行执行多个异步操作,并且只有在所有操作都完成后才继续执行后续代码。这在需要同时获取多个数据源的数据时特别有用。
const fetchData1 = fetch('/api/data1');
const fetchData2 = fetch('/api/data2');
const fetchData3 = fetch('/api/data3');
Promise.all([fetchData1, fetchData2, fetchData3])
.then(responses => {
return Promise.all(responses.map(response => response.json()));
})
.then(data => {
console.log(data); // [data1, data2, data3]
})
.catch(error => {
console.error('Error fetching data:', error);
});
2. 处理多个独立的异步操作
在某些情况下,我们可能需要执行多个彼此独立的异步操作,并在它们全部完成后进行处理。Promise.all
可以很好地解决这个问题。
const processImage = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'Image processed');
});
const uploadImage = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'Image uploaded');
});
const generateThumbnail = new Promise((resolve, reject) => {
setTimeout(resolve, 1500, 'Thumbnail generated');
});
Promise.all([processImage, uploadImage, generateThumbnail])
.then(results => {
console.log(results); // ["Image processed", "Image uploaded", "Thumbnail generated"]
})
.catch(error => {
console.error('Error:', error);
});
手写Promise.all
function myPromiseAll(promises) {
if (!Array.isArray(promises)) {
return Promise.reject(new Error('promises must be an array'))
}
let cnt = 0, res = []
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
// 使用 Promise.resolve 包装它,以确保即使传入的是非 Promise 值(如普通值),它也会被转换成一个已经解决的 Promise。
Promise.resolve(promise).then((result) => {
// 如果成功,将结果存入 res 数组,并增加计数器
cnt++
res.push(result)
// 如果所有 promise 都已解决,则调用 resolve
if (cnt === promises.length) {
resolve(res)
}
},
// 如果失败,则立即调用 reject
(error) => {
reject(error)
})
});
})
}