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();我思考了很久,最后还是从黄玄大佬的知乎回答中窥见的一部分原理。