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));