// 示例
async function fn() {
let value = await new Promise((resolve, reject) => {
reject('failure');
});
console.log('do something...');
}
fn()
复制代码
导致浏览器报错:一个未捕获的错误
在开发过程中,为了保证系统健壮性,或者是为了捕获异步的错误,需要频繁的在 async 函数中添加 try/catch,避免出现上述示例的情况
可是我很懒,不想一个个加,懒惰使我们进步😂
下面,通过手写一个babel 插件,来给所有的async函数添加try/catch
babel插件的最终效果
原始代码:
async function fn() {
await new Promise((resolve, reject) => reject('报错'));
await new Promise((resolve) => resolve(1));
console.log('do something...');
}
fn();
复制代码
使用插件转化后的代码:
async function fn() {
try {
await new Promise((resolve, reject) => reject('报错'));
await new Promise(resolve => resolve(1));
console.log('do something...');
} catch (e) {
console.log("\nfilePath: E:\\myapp\\src\\main.js\nfuncName: fn\nError:", e);
}
}
fn();
复制代码
打印的报错信息:
通过详细的报错信息,帮助我们快速找到目标文件和具体的报错方法,方便去定位问题
babel插件的实现思路
1)借助AST抽象语法树,遍历查找代码中的await关键字
2)找到await节点后,从父路径中查找声明的async函数,获取该函数的body(函数中包含的代码)
3)创建try/catch语句,将原来async的body放入其中
4)最后将async的body替换成创建的try/catch语句
babel的核心:AST
先聊聊 AST 这个帅小伙🤠,不然后面的开发流程走不下去
AST是代码的树形结构,生成 AST 分为两个阶段:词法分析和 语法分析
词法分析
词法分析阶段把字符串形式的代码转换为令牌(tokens) ,可以把tokens看作是一个扁平的语法片段数组,描述了代码片段在整个代码中的位置和记录当前值的一些信息
比如let a = 1,对应的AST是这样的
语法分析
语法分析阶段会把token转换成 AST 的形式,这个阶段会使用token中的信息把它们转换成一个 AST 的表述结构,使用type属性记录当前的类型
例如 let 代表着一个变量声明的关键字,所以它的 type 为 VariableDeclaration,而 a = 1 会作为 let 的声明描述,它的 type 为 VariableDeclarator
AST在线查看工具:AST explorer
再举个🌰,加深对AST的理解
function demo(n) {
return n * n;
}
复制代码
转化成AST的结构
{
"type": "Program", // 整段代码的主体
"body": [
{
"type": "FunctionDeclaration", // function 的类型叫函数声明;
"id": { // id 为函数声明的 id
"type": "Identifier", // 标识符 类型
"name": "demo" // 标识符 具有名字
},
"expression": false,
"generator": false,
"async": false, // 代表是否 是 async function
"params": [ // 同级 函数的参数
{
"type": "Identifier",// 参数类型也是 Identifier
"name": "n"
}
],
"body": { // 函数体内容 整个格式呈现一种树的格式
"type": "BlockStatement", // 整个函数体内容 为一个块状代码块类型
"body": [
{
"type": "ReturnStatement", // return 类型
"argument": {
"type": "BinaryExpression",// BinaryExpression 二进制表达式类型
"start": 30,
"end": 35,
"left": { // 分左 右 中 结构
"type": "Identifier",
"name": "n"
},
"operator": "*", // 属于操作符
"right": {
"type": "Identifier",
"name": "n"
}
}
}
]
}
}
],
"sourceType": "module"
}