Promise.all() 原理解析及使用指南

21,479 阅读3分钟

Promise 对象是ECMAScript 6中新增的对象,主要将 JavaScript 中的异步处理对象和处理规则进行了规范化。前面介绍了《Promise.any() 原理解析及使用指南》,本文来介绍另一个方法 Promise.all(promises) ,能够一次并行处理多个 promise,并且只返回一个 promise 实例, 那个输入的所有 promiseresolve 回调的结果是一个数组。

下面来看看 Promise.all() 是如何工作的。

1.工作原理

Promise.all() 是一个内置的辅助函数,接受一组 promise(或者一个可迭代的对象),并返回一个promise

const allPromise = Promise.all([promise1, promise2, ...]);

可以使用 then 方法提取第一个 promise 的值:

allPromise.then((values) => {
    values; // [valueOfPromise1, valueOfPromise2, ...]
});

也可以使用 async/await 语法:

const values = await allPromise;
console.log(values); // [valueOfPromise1, valueOfPromise2, ...]

Promise.all() 返回的 promise 被解析或拒绝的方式。

如果 allPromise 都被成功解析,那么 allPromise 将使用一个包含各个 promise 已执行完成后的值的数组作为结果。数组中 promise 的顺序是很重要的——将按照这个顺序得到已实现的值。

20-1.jpg

但是如果至少有一个 promiserejected ,那么 allPromise 会以同样的原因立即 rejected (不等待其他 promise 的执行)。

20-2.jpg

如果所有的 promiserejected ,等待所有的promise 执行完成,但只会返回最先被rejectedpromisereject 原因。

20-3.jpg

2. 使用指南

现在来深入介绍一下 Promise.all(), 在这之前,先来定义 2 个简单的函数。

函数 resolveTimeout(value, delay) 将返回一个在经过 delay 时间后有 resolvepromise

function resolveTimeout(value, delay) {
    return new Promise((resolve) => setTimeout(() => resolve(value), delay));
}

函数 rejectTimeout(reason, delay) 将返回一个在经过 delay 时间后有 rejectpromise

function rejectTimeout(reason, delay) {
    return new Promise((r, reject) => setTimeout(() => reject(reason), delay));
}

接下来使用上面定义的2个辅助函数来试试 Promise.all()

2.1 完成所有 promises

下面定义了一个 promise 数组 allPromise ,所有的 promise 都能够成功的 resolve 值,如下:

function resolveTimeout(value, delay) {
    return new Promise((resolve) => setTimeout(() => resolve(value), delay));
}
const fruits = ["potatoes", "tomatoes"];
const vegetables = ["oranges", "apples"];

const allPromise = [
    resolveTimeout(fruits, 2000),
    resolveTimeout(vegetables, 1000),
];
const promise = Promise.all(allPromise);

// 等待... 2秒后
const list = async () => {
    try {
        const result = await promise;
        console.log(result);
    } catch (error) {
        console.log(error.errors);
    }
};

list(); // [ [ 'potatoes', 'tomatoes' ], [ 'oranges', 'apples' ] ]

从上面执行的结果来看 Promise.all() 返回的 promiseresolve 数组是按照执行前 allPromise 的顺序组成其结果。

promise 数组的顺序直接影响结果的顺序,和 promise 执行完成的先后无关。

2.2 一个 promiserejected

将上面数组 allPromise 的第一个 promise 出现异常被 rejected ,如下代码:

const promise = Promise.all([
    rejectTimeout(new Error("fruits is empty"), 5000),
    resolveTimeout(vegetables, 1000),
]);

// 等待...
const list = async () => {
    try {
        const result = await promise;
        console.log(result);
    } catch (error) {
        console.log(error);
    }
};

list(); // Error: fruits is empty

然而,在经过 5秒 之后,第一个 promise 由于异常被 rejected ,使得 allPromise 也被 rejected ,并返回跟第一个 promise 一样的错误信息:Error: fruits is empty ,即使在 1秒 后就完成的第二个 promise 的值也不被采纳。

接下来将数组 allPromise 的所有 promise 都抛出异常被 rejected ,通过定时器将 rejected 的顺序做个调整,如下:

const promise = Promise.all([
    rejectTimeout(new Error("fruits is empty"), 5000),
    rejectTimeout(new Error("vegetables is empty"), 1000),
]);

// 等待...
const list = async () => {
    try {
        const result = await promise;
        console.log(result);
    } catch (error) {
        console.log(error);
    }
};

经过 5秒 之后完成执行,而结果显示为 Error: vegetables is empty ,不难看出 allPromiserejected 的原因是最先 rejectedpromise

Promise.all() 的这种行为被称为快速失败,如果 promise 数组中至少有一个 promiserejected ,那么返回的 promise 也被拒绝。如果promise 数组中所有的都被 rejected ,那么返回的promise 被拒绝的原因是先rejected的那一个。

总结

Promise.all() 是并行执行异步操作并获取所有 resolve 值的最佳方法,非常适合需要同时获取异步操作结果来进行下一步运算的场合。