- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第21期,await-to-js 如何优雅的捕获 await 的错误。
好了,补充完该文章诞生的缘由,那么就开始文章的内容吧!
1.介绍
简单说明下使用await-to-js插件的目的。 在es6之前,我们处理异步嵌套操作(最明显的就是请求接口)都是基于回调函数的,如下:
var fs = require("fs");
fs.readFile("./src/a.txt", "utf-8", function (err, data) {
if (err) {
throw err;
}
console.log(data);
fs.readFile("./src/b.txt", "utf-8", function (err, data) {
if (err) {
throw err;
}
console.log(data);
fs.readFile("./src/c.txt", "utf-8", function (err, data) {
if (err) {
throw err;
}
console.log(data);
});
});
});
很明显,当异步嵌套时,只能通过这样的回调函数形式去执行,但是如果业务非常复杂,那么难道我们要一直这么嵌套下去?es6之前我们确实是这么做的,但是es6开始有了新的解决方法,那就是Promise。
使用Promise:
// (1)创建三个异步操作 promise
const p1 = new Promise((resolve, reject) => {
//读文件
fs.readFile("./src/a.txt", "utf8", (err, data) => {
if (err == null) {
//成功
resolve(data);
} else {
//失败
reject(err);
}
});
});
//读取文件B
const p2 = new Promise((resolve, reject) => {
//读文件
fs.readFile("./src/b.txt", "utf8", (err, data) => {
if (err == null) {
//成功
resolve(data);
} else {
//失败
reject(err);
}
});
});
//读取文件C
const p3 = new Promise((resolve, reject) => {
//读文件
fs.readFile("./scr/c.txt", "utf8", (err, data) => {
if (err == null) {
//成功
resolve(data);
} else {
//失败
reject(err);
}
});
});
// (2)按照顺序处理异步操作结果
p1.then((data) => {
return p2;
}).then((data) => {
return p3;
}).then((data) => {
console.log(data);
}).cacth(err=>{
console.log(err);
});
相信大家都对Promise有足够的了解,这里就不过多的阐释Promise的用法,读者也参考我之前写的关于手写Promise的文章,点击了解详情。
后来出现了async和await的方式优雅的处理Promise链的操作,异步转同步,一旦遇到await,则async函数中await下面的代码都会被阻塞,直到await等待到结果,await一般紧跟着的是Promise函数,当然也可以跟同步代码,不过跟同步没啥实际意义。
当然本文也不是讲async和await的使用,只是为了引出async和await对于错误捕获的结构,才费了这么多笔墨,话不多说,看代码。
function getData(data) {
return new Promise((resolve, reject) => {
if (data === 1) {
setTimeout(() => {
resolve("getdata success");
}, 1000);
} else {
setTimeout(() => {
reject("getdata error");
}, 1000);
}
});
}
try {
let res = await getData(3);
console.log(res);
} catch (error) {
console.log(res);
}
我们可以看到,针对Promise的错误捕获是使用try + catch的,该结构其实已经很清晰明了了,但是有没有发现,我们为了捕获错误多了很多行代码?
//不捕获:
let res = await getData(3);
console.log(res);
//捕获:
try {
let res = await getData(3);
console.log(res);
} catch (error) {
console.log(res);
}
那如果有很多个Promise需要捕获异常呢?那是否需要写n个try + catch ?
嗯?好像可以最外层套一个try + catch,把所有Promise函数都套里面啊,一次性捕获!
但是,理想很丰满,现实很骨感,因为我们经常会针对每一个异常捕获做其他逻辑的处理,一次性捕获显然并不合适。
好了,说了这么多,就是为了引出今天的主角:await-to-js。 好吧,这个介绍的篇幅有点长了,可是真的不是在水......
2.await-to-js源码分析
await-to-js的源码真的是非非非常的短,只有短短的22行,请看:
/**
* @param { Promise } promise
* @param { Object= } errorExt - Additional Information you can pass to the err object
* @return { Promise }
*/
export function to<T, U = Error>( //泛型T和U,U默认Error类型
promise: Promise<T>, //接收Promise对象参数,该Promise对象返回值也是T
errorExt?: object //可选参数,补充错误的对象,类型object
): Promise<[U, undefined] | [null, T]> { //to函数返回值错误时=[U,undefined],成功时=[null,data],data显然也为T
return promise
.then<[null, T]>((data: T) => [null, data]) //成功返回[null,data]
.catch<[U, undefined]>((err: U) => {
if (errorExt) { //如果第二个错误的参数有,则对错误进行合并,并返回
const parsedError = Object.assign({}, err, errorExt);
return [parsedError, undefined];
}
return [err, undefined]; //如果第二个参数无,则直接返回[err, undefined]
});
}
export default to;
//当然,因为不知道用户采用何种方式导入,因此默认导出和指定导出都export了
鉴于大家可能不太熟悉ts(我自己也不熟悉T~T),我还是简单描述一下(描述已注释在代码中,熟悉的大佬不要喷我的卑微描述)。
然后...,还是卑微的贴一下对应的js代码吧。
/**
* @param { Promise } promise
* @param { Object= } errorExt - Additional Information you can pass to the err object
* @return { Promise }
*/
function to(promise, errorExt) {
return promise
.then(function (data) {
return [null, data];
})
.catch(function (err) {
if (errorExt) {
var parsedError = Object.assign({}, err, errorExt);
return [parsedError, undefined];
}
return [err, undefined];
});
}
// export default to;
module.exports = to;
//# sourceMappingURL=await-to-js.js.map
接下来我们测试一下这个方法的使用。
3.to方法的使用
当然,如果大家拉了源码,也可以在examples目录下找到对应官方提供的测试案例,由于官方案例写了稍微复杂了那么一丢丢,我就用自己简单的测试案例了。
async function test() {
const [err1, data1] = await to(Promise.resolve("这是成功的测试"));
// ...可以对错误或者data处理...
console.log(err1, data1); // null 这是成功的测试
const [err2, data2] = await to(Promise.reject("这是失败的测试"));
// ...可以对错误或者data处理...
console.log(err2, data2); // 这是失败的测试 undefined
}
test();
这么一看,代码是不是简洁的很多?只要一行代码就同时拿到了错误和结果,顿时赏心悦目了起来。
4.总结和心得
本人参与源码阅读的时间不长,看过的期数也屈指可数,但是不管怎样,也是走在进步的道路上(虽然技术依旧很菜)。
之前在某群里聊天,有群友发展比较好,某群友就表达了羡慕之情,然后回去依旧刷刷电视玩玩手机,我跟她说:“光羡慕却又不努力是没用的。”这句话,我也送给自己。
当然了,做人嘛,当然是要开心呐。很显然,学习就是一件开心的事情啊,至少知道自己在做什么,明确知道自己在朝着巨人的膝盖攀爬,尽管,跟那些站在巨人肩膀上的大佬依旧无法比拟。。。
好了,那就出发吧!(相信可能有小伙伴已经联想到了某首歌)
5.结尾有彩蛋
axios.post("/路径",data).then(res=>{
// n多处理逻辑
//......
}).catch(err=>{})
不知道有没有大佬维护过或者写过这样的代码,catch捕获了错误,又不对错误进行输出或者抛出错误,然后n久以后,发现页面显示异常,但是控制台又没有错误信息或者日志,然后很懵逼的去到处找错误,最后打断点才发现n条处理逻辑中,因为后台数据格式改变,或者其余边界情况问题导致的代码错误......。
好吧,这确实很坑啊。