闽公网安备 35020302035485号
const asyncErrorThrow = () => {
return new Promise((resolve, reject) => {
// 业务代码...
// 假设这里抛出了错误
throw new Error('抛出错误');
// 业务代码...
})
}
const testFun = async () => {
await asyncErrorThrow();
console.log("async 函数中的后续流程"); // 不会执行
}
testFun();
在 testFun 函数中,抛出错误后,await 函数中后续流程不会执行。仔细回想一下,在我的前端日常开发中,对于错误捕获,还基本停留在使用 Promise时用 catch 捕获一下 Promise 中抛出的错误或者 reject,或者最基本的,在使用 JSON.parse、JSON.stringfy等容易出错的方法中,使用 try..catch... 方法捕获一下可能出现的错误。const asyncErrorThrow = () => {
return new Promise((resolve, reject) => {
// 堆代码 duidaima.com
// 业务代码...
throw new Error('抛出错误');
// 业务代码...
})
}
const testFun = async () => {
try {
await asyncErrorThrow();
console.log("async 函数中的后续流程"); // 不会执行
} catch (error) {
console.log("若错误发生 async 函数中的后续流程"); // 会执行
}
}
testFun();
而这次不同的是,这段修改后的代码中使用了 try...catch...来捕获 async...await... 函数中的错误,这着实让我有些困惑,让我来写的话,我可能会在 await 函数的后面增加一个 catch:await asyncErrorThrow().catch(error => {})。因为我之前已经对 try..catch 只能捕获发生在当前执行上下文的错误(或者简单理解成同步代码的错误)有了一定的认知,但是 async...await... 其实还是异步的代码,只不过用的是同步的写法,为啥用在这里就可以捕获到错误了呢?在查阅了相当多的资料之后,才清楚了其中的一些原理。已拒绝(rejected):意味着操作失败。
const function myExecutorFunc = () => {
// 同步代码
throw new Error();
};
new Promise(myExecutorFunc);
Promise 的构造函数需要传入的 Executor 函数参数,实际上是一段同步代码。在我们 new 一个新的 Promise 时,这个 Executor 就会立即被塞入到当前的执行上下文栈中进行执行。但是,在 Executor 中 throw 出的错误,并不会被外层的 try...catch 捕获到。const myExecutorFunc = () => {
// 同步代码
throw new Error();
};
try {
new Promise(myExecutorFunc);
} catch (error) {
console.log('不会执行: ', error);
}
console.log('会执行的'); // 打印
其原因是因为,在 Executor 函数执行的过程中,实际上有一个隐藏的机制,当同步抛出错误时,相当于执行了 reject 回调,让该 Promise 进入 rejected 状态。而错误不会影响到外层的代码执行。const myExecutorFunc = () => {
throw new Error();
// 等同于
reject(new Error());
};
new Promise(myExecutorFunc);
console.log('会执行的'); // 打印
同理 then 回调函数也是这样的,抛出的错误同样会变成 reject。new Promise(function() {
throw new Error("");
}); // 没有用来处理 error 的 catch
// Web 标准实现
window.addEventListener('unhandledrejection', function(event) {
console.log(event);
// 可以在这里采取其他措施,如日志记录或应用程序关闭
});
// Node 下的实现
process.on('unhandledRejection', (event) => {
console.log(event);
// 可以在这里采取其他措施,如日志记录或应用程序关闭
});
Promise 是这样实现的,我们可以想一想为什么要这样实现。我看到一个比较好的回答是这个:const asyncErrorThrow = () => {
return new Promise((resolve, reject) => {
// 业务代码...
throw new Error('抛出错误');
// 业务代码...
})
}
const testFun = async () => {
try {
await asyncErrorThrow();
console.log("async 函数中的后续流程"); // 不会执行
} catch (error) {
console.log("若错误发生 async 函数中的后续流程"); // 会执行
}
}
testFun();
我思考了很久,最后还是从黄玄大佬的知乎回答中窥见的一部分原理。
