Open adodo0829 opened 2 years ago
// 一次异步任务中断引起的思考 // 单个异步请求的异常捕获: 在promise.catch()中获取, 做对应的逻辑处理 // 上一个请求异常, 中断后面的异步任务, 在promise.catch()能中断吗? 不能, 这个属于微任务队列里内部逻 // 问题 async function getData() { const data1 = await getData1(); // 我想在data1出错的时候中断后面的任务 const data2 = await getData2(); handleData(data1, data2); } // 1.异步任务异常处理和外层中断: setTimeout, setInterval, // setTimeout, setInterval对应的是事件循环中的宏任务 try { setTimeout(() => { let a = c; }, 100); } catch (e) { console.log("能获取到错误么??", e); // 不能, why? } // 这样能吗? try { setTimeout(() => { try { let a = c; } catch (e) { throw new Error("some variable is not defined"); } }, 100); } catch (e) { console.log("能获取到错误么??", e); // 不能, why? } // 根本原因还是js的事件循环, 一个事件循环包含一个或多个任务队列, // 每一个任务队列里的任务是严格按照先进先出的顺序执行的,但是不同任务队列的任务的执行顺序是不确定的。 // 类似这种队列结构: [[宏任务1,微任务1,微任务2...], [宏任务2,..], [宏任务3,...]] /** * 任务类型 * macrotasks script(整体代码), setTimeout, setInterval, * microtasks Promise */ /** * 事件循环的进程模型 * // 备注: 下面任务的执行是要放到执行栈里去的, 对应了不同上下文 * * 1.选择当前要执行的任务队列,选择一个最先进入任务队列的任务,如果没有任务可以选择,则会跳转至5,microtask的执行步骤。 * 2.将事件循环的当前运行任务设置为已选择的任务。 * 3.运行当前任务。 * 4.将事件循环的当前运行任务设置为null。 * 4.1-将运行完的任务从任务队列中移除 * 5.检查microtasks:进入microtask检查点(performing a microtask checkpoint )。 * 5.1-设置进入microtask检查点的标志为true * 5.2-当事件循环的微任务队列不为空时:选择一个最先进入microtask队列的microtask * 5.3-设置事件循环的当前运行任务为已选择的microtask * 5.4-运行microtask; * 5.5-设置事件循环的当前运行任务为null * 5.6-将运行结束的microtask从microtask队列中移除 * 6.更新界面渲染。 * 7.返回第一步 */ // 回顾上面的问题 /** * 最外层的try catch是在一个task中,我们定义它为我们js文件的同步主任务,从上到下执行到这里了, 然后,会把里面的setTimeout推到一个任务队列中, 这个队列是存储在内存中的。 * 然后主task就继续向下执行, 一直到结束。 * 当该setTimeout时间到了,且没有其它的task执行了, 那么,就将这个setTimeout的代码推入执行栈开始执行。 当执行到错误代码的时候,也就是这个 let a = c, 因为c未定义,所以就会报错。 * 但问题的本质是,这个错误跟最外层的try catch并不在一个执行栈中,当里面执行的时候,外边的这个task早已执行完, 他们的context(上下文)已经完全不同了 */ // 2.异步任务异常处理和外层中断: promise, // Promise 也是一个异步的处理过程,它对应事件循环中的微任务 try { new Promise((resolve, reject) => { reject("promise error"); }); } catch (e) { console.log("异步错能catch到吗?", e); // 不能, 执行栈上下文已经不同了 } try { new Promise((resolve, reject) => { reject("promise error"); }).catch((e) => { // 这样能把错误传到外面吗 throw new Error(e); }); } catch (e) { console.log("异步错能catch到吗?", e); // 也是不行的 } // 3.Async await, 通过异步等待的方式,用try catch可以吗 // 本质上也是微任务, 但是await使用生成器函数 对上下文进行暂停 const asyncFn = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject("asyncFn执行时出现错误了"); }, 100); }); }; const executedFn = async () => { try { await asyncFn(); } catch (e) { // 能捕获错误吗? console.log("捕获错误", e); // 能 } // asyncFn里面是有 Promise的,为什么外边就能catch到了呢 // async-await 是使用生成器、promise 和协程实现的,wait操作符还存储返回事件循环之前的执行上下文,以便允许promise操作继续进行。 // 当内部通知解决等待的承诺时,它会在继续之前恢复执行上下文。 所以说,能够回到最外层的上下文, 那就可以用try catch }; async function asyncLogic() { try { const data1 = await getData1(); breakLoop(); const data2 = await getData2(); breakLoop(); const data3 = await getData3(); breakLoop(); // 处理数据 handle(data1, data2, data3); } catch (err) { if (err === "interrupt") { return; } // 处理其他业务错误 throw err; } function checkAbort() { // 满足某个条件 if (condition) { // 抛出的功能在 catch 中检查出来就行 throw "interrupt"; } } }