Promise.all()
是一种方法,它接受一个可迭代的元素(通常是Promises
)作为输入,并返回一个Promise
,该Promise
解析为输入 promise 的结果数组。当所有输入的 promise 都已解决时,或者如果输入可迭代对象不包含任何 promise 时,此返回的 promise 将被解析。它会在任何输入 promise 拒绝或非 promise 抛出错误时立即拒绝,并将使用第一个拒绝消息/错误拒绝。
来源:Promise.all() - JavaScript | MDN
Promise.all()
经常用于有多个并发 API 请求,并且我们希望等待所有请求完成才能继续执行代码,通常是因为我们依赖于两个响应的数据。
const [userData, postsData, tagsData] = await Promise.all([fetch('/api/user'),fetch('/api/posts'),fetch('/api/tags'),]);
让我们实现我们自己的 Promise.all()
版本,一个 promiseAll
函数,不同之处在于该函数接受一个数组而不是一个可迭代对象。请务必仔细阅读说明并相应地实现!
// 已解决的示例。const p0 = Promise.resolve(3);const p1 = 42;const p2 = new Promise((resolve, reject) => {setTimeout(() => {resolve('foo');}, 100);});await promiseAll([p0, p1, p2]); // [3, 42, 'foo']
// 拒绝示例。const p0 = Promise.resolve(30);const p1 = new Promise((resolve, reject) => {setTimeout(() => {reject('发生错误!');}, 100);});try {await promiseAll([p0, p1]);} catch (err) {console.log(err); // '发生错误!'}
Promise.all()
是一种方法,它接受一个可迭代的元素(通常是Promises
)作为输入,并返回一个Promise
,该Promise
解析为输入 promise 的结果数组。当所有输入的 promise 都已解决时,或者如果输入可迭代对象不包含任何 promise 时,此返回的 promise 将被解析。它会在任何输入 promise 拒绝或非 promise 抛出错误时立即拒绝,并将使用第一个拒绝消息/错误拒绝。
来源:Promise.all() - JavaScript | MDN
Promise.all()
经常用于有多个并发 API 请求,并且我们希望等待所有请求完成才能继续执行代码,通常是因为我们依赖于两个响应的数据。
const [userData, postsData, tagsData] = await Promise.all([fetch('/api/user'),fetch('/api/posts'),fetch('/api/tags'),]);
让我们实现我们自己的 Promise.all()
版本,一个 promiseAll
函数,不同之处在于该函数接受一个数组而不是一个可迭代对象。请务必仔细阅读说明并相应地实现!
// 已解决的示例。const p0 = Promise.resolve(3);const p1 = 42;const p2 = new Promise((resolve, reject) => {setTimeout(() => {resolve('foo');}, 100);});await promiseAll([p0, p1, p2]); // [3, 42, 'foo']
// 拒绝示例。const p0 = Promise.resolve(30);const p1 = new Promise((resolve, reject) => {setTimeout(() => {reject('发生错误!');}, 100);});try {await promiseAll([p0, p1]);} catch (err) {console.log(err); // '发生错误!'}
这是一个非常重要的问题,需要在面试中进行练习,因为异步编程经常在面试中被考察。了解 Promise.all
的工作原理将帮助你理解类似 Promise
相关函数(如 Promise.race
、Promise.any
、Promise.allSettled
等)背后的机制。
关于这个问题,我们需要牢记并处理以下几个方面:
Promise
旨在被链式调用,因此该函数需要返回一个 Promise
。Promise
将使用一个空数组进行解析。Promise
包含一个已解析值的数组,其顺序与输入相同。Promise
将立即被拒绝。Promise
。async
统计未解析的 promise由于该函数需要返回一个 Promise
,我们可以在该函数的顶层构造一个 Promise
并返回它。大部分代码将写在构造函数参数中。
我们首先检查输入数组是否为空,如果是,则使用一个空数组进行解析。
然后我们需要尝试解析输入数组中的每个项目。这可以使用 Array.prototype.forEach
或 Array.prototype.map
来实现。由于返回值需要保留输入数组的顺序,我们创建一个 results
数组,并使用其在输入数组中的 index
将值放入正确的位置。为了知道何时解析了所有输入数组值,我们通过初始化一个未解析值的计数器并在每次解析一个值时将其递减来跟踪未解析的 promise 的数量。当计数器达到 0 时,我们可以返回 results
数组。
这里需要注意的一点是,由于输入数组可以包含非 Promise
值,如果我们没有 await
它们,我们需要使用 Promise.resolve()
包装每个值,这允许我们在它们上面使用 .then()
,并且我们不必区分 Promise
与非 Promise
值以及它们是否需要被解析。
最后,如果任何值被拒绝,我们将立即拒绝顶层 Promise
,而无需等待任何其他待处理的 promise。
/*** @param {Array} iterable* @return {Promise<Array>}*/export default function promiseAll(iterable) {return new Promise((resolve, reject) => {const results = new Array(iterable.length);let unresolved = iterable.length;if (unresolved === 0) {resolve(results);return;}iterable.forEach(async (item, index) => {try {const value = await item;results[index] = value;unresolved -= 1;if (unresolved === 0) {resolve(results);}} catch (err) {reject(err);}});});}
Promise.then
统计未解析的 promise如果你不喜欢使用 async
/await
,这里有一个使用 Promise.then()
的替代版本。
/*** @param {Array} iterable* @return {Promise<Array>}*/export default function promiseAll(iterable) {return new Promise((resolve, reject) => {const results = new Array(iterable.length);let unresolved = iterable.length;if (unresolved === 0) {resolve(results);return;}iterable.forEach((item, index) => {Promise.resolve(item).then((value) => {results[index] = value;unresolved -= 1;if (unresolved === 0) {resolve(results);}},(reason) => {reject(reason);},);});});}
一旦其中一个 Promise
的解析函数(resolve
或 reject
)被调用,promise 就处于“已确定”状态,并且对任一函数的后续调用既不能更改实现值或拒绝原因,也不能将最终状态从“已实现”更改为“已拒绝”,反之亦然。
Promise
值,如果所有输入值都已实现,它们仍将是返回数组的一部分。Promise
,如何构建和使用它们。console.log()
语句将显示在此处。