1、Promise.all 的错误处理
Promise.all 方法接受一个 Promise 数组,并返回所有解析 Promise 的结果数组:
const promise1 = Promise.resolve("one");
const promise2 = Promise.resolve("two");Promise.all([promise1, promise2]).then((results) => console.log(results));// 结果: ['one', 'two']
如果这些 Promise 中的任何一个被拒绝,Promise.all 将拒绝并返回第一个被拒绝的 Promise 的错误。
为了在 Promise.all 中处理这些情况,可以使用 catch:
const promise1 = Promise.resolve("good");
const promise2 = Promise.reject(Error("Bad"));
const promise3 = Promise.reject(Error("Bad+"));Promise.all([promise1, promise2, promise3]).then(results => console.log(results)).catch(error => console.error(error.message));
如果想要运行一个函数而不考虑 Promise.all 的结果,可以使用 finally:
Promise.all([promise1, promise2, promise3]).then(results => console.log(results)).catch(error => console.error(error.message)).finally(() => console.log("Finally"));
2、Promise.any 的错误处理
Promise.any 和 Promise.all 恰恰相反。Promise.all 如果某一个失败,就会抛出第一个失败的错误。而 Promise.any 总是返回第一个成功的 Promise,无论是否发生任何拒绝。
相反,如果传递给 Promise.any 的所有 Promise 都被拒绝,那产生的错误就是 AggregateError。 来看下面的例子:
const promise1 = Promise.reject(Error("Error"));
const promise2 = Promise.reject(Error("Error+"));Promise.any([promise1, promise2]).then(result => console.log(result)).catch(error => console.error(error)).finally(() => console.log("Finally"));
输出结果如下:
这里用 catch 处理错误。AggregateError 对象具有与基本错误相同的属性,外加一个 errors 属性:
const promise1 = Promise.reject(Error("Error"));
const promise2 = Promise.reject(Error("Error+"));Promise.any([promise1, promise2]).then(result => console.log(result)).catch(error => console.error(error.errors)).finally(() => console.log("Finally"));
此属性是一个包含所有被拒绝的错误信息的数组:
3、Promise.race 的错误处理
Promise.race 接受一个 Promise 数组,并返回第一个成功的 Promise 的结果:
const promise1 = Promise.resolve("one");
const promise2 = Promise.resolve("two");Promise.race([promise1, promise2]).then(result => console.log(result)
);// 结果:one
那如果有被拒绝的 Promise,但它不是传入数组中的第一个呢:
const promise1 = Promise.resolve("one");
const rejection = Promise.reject(Error("Bad"));
const promise2 = Promise.resolve("two");Promise.race([promise1, rejection, promise2]).then(result =>console.log(result)
);// 结果:one
这样结果还是 one,不会影响正常的执行。
如果被拒绝的 Promise 是数组的第一个元素,则 Promise.race 拒绝,就必须要必须捕获拒绝:
const promise1 = Promise.resolve("one");
const rejection = Promise.reject(Error("Bad"));
const promise2 = Promise.resolve("two");Promise.race([rejection, promise1, promise2]).then(result => console.log(result)).catch(error => console.error(error.message));// Bad
4、Promise.allSettled 的错误处理
Promise.allSettled 是 ECMAScript 2020 新增的 API。它和 Promise.all 类似,不过不会被短路,也就是说当 Promise 全部处理完成后,可以拿到每个 Promise 的状态, 而不管其是否处理成功。
来看下面的例子:
const promise1 = Promise.resolve("Good!");
const promise2 = Promise.reject(Error("Bad!"));Promise.allSettled([promise1, promise2]).then(results => console.log(results)).catch(error => console.error(error)).finally(() => console.log("Finally"));
这里向 Promise.allSettled 传递了一个包含两个 Promise 的数组:一个已解决,另一个已拒绝。
输出结果如下:
5、async/await 的错误处理
JavaScript 中的 async/await 表示异步函数,用同步的方式去编写异步,可读性更好。
下面来改编上面的同步函数 toUppercase,通过将 async 放在 function 关键字之前将其转换为异步函数:
async function toUppercase(string) {if (typeof string !== "string") {throw TypeError("Expected string");}return string.toUpperCase();
}
只需在 function 前加上 async 前缀,就可以让函数返回一个 Promise。这意味着我们可以在函数调用之后链式调用 then、catch 和 finally:
toUppercase("hello").then(result => console.log(result)).catch(error => console.error(error.message)).finally(() => console.log("Always runs!"));
当从 async 函数中抛出异常时,异常会成为底层 Promise 被拒绝的原因。任何错误都可以从外部用 catch 拦截。
除此之外,还可以使用 try/catch/finally 来处理错误,就像在同步函数中一样。
例如,从另一个函数 consumer 中调用 toUppercase,它方便地用 try/catch/finally 包装了函数调用:
async function toUppercase(string) {if (typeof string !== "string") {throw TypeError("Expected string");}return string.toUpperCase();
}async function consumer() {try {await toUppercase(98);} catch (error) {console.error(error.message);} finally {console.log("Finally");}
}consumer();
输出结果如下:
6、异步生成器的错误处理
JavaScript 中的异步生成器是能够生成 Promise 而不是简单值的生成器函数。它将生成器函数与异步相结合,结果是一个生成器函数,其迭代器对象向消费者公开一个 Promise。
要创建一个异步生成器,需要声明一个带有星号 * 的生成器函数,前缀为 async:
async function* asyncGenerator() {yield 33;yield 99;throw Error("Bad!"); // Promise.reject
}
因为异步生成器是基于 Promise,所以同样适用 Promise 的错误处理规则,在异步生成器中,throw 会导致 Promise 拒绝,可以用 catch 拦截它。
要想从异步生成器处理 Promise,可以使用 then:
const go = asyncGenerator();go.next().then(value => console.log(value));
go.next().then(value => console.log(value));
go.next().catch(reason => console.error(reason.message));
输出结果如下:
也使用异步迭代 for await...of。 要使用异步迭代,需要用 async 函数包装 consumer:
async function* asyncGenerator() {yield 33;yield 99;throw Error("Bad"); // Promise.reject
}async function consumer() {for await (const value of asyncGenerator()) {console.log(value);}
}consumer();
与 async/await 一样,可以使用 try/catch 来处理任何异常:
async function* asyncGenerator() {yield 33;yield 99;throw Error("Bad"); // Promise.reject
}async function consumer() {try {for await (const value of asyncGenerator()) {console.log(value);}} catch (error) {console.error(error.message);}
}consumer();
输出结果如下:
从异步生成器函数返回的迭代器对象也有一个 throw()
方法。在这里对迭代器对象调用 throw() 不会抛出异常,而是 Promise 拒绝:
async function* asyncGenerator() {yield 33;yield 99;yield 11;
}const go = asyncGenerator();go.next().then(value => console.log(value));
go.next().then(value => console.log(value));go.throw(Error("Reject!"));go.next().then(value => console.log(value));
输出结果如下:
可以通过以下方式来捕获错误:
go.throw(Error("Let's reject!")).catch(reason =>console.error(reason.message)
);
我们知道,迭代器对象的 throw() 是在生成器内部发送异常的。所以还可以使用以下方式来处理错误:
async function* asyncGenerator() {try {yield 33;yield 99;yield 11;} catch (error) {console.error(error.message);}
}const go = asyncGenerator();go.next().then(value => console.log(value));
go.next().then(value => console.log(value));go.throw(Error("Reject!"));go.next().then(value => console.log(value));