await-to-js 源码分析,体验一把捕获异常的优雅
theme: cyanosis highlight: atelier-dune-light
前言
await-to-js 的源码较少,代码较少的情况,可以尝试采用 3W 方法进行源码分析,帮助快速了解它的实际应用场景。
阅读本文,会有以下收获:
- 了解 await-to-js 库能解决什么问题。
- 分析 await-to-js 库是如何解决问题。
- 摸索 await-to-js 库的实际使用场景。
Why:await-to-js 诞生的契机
打开它的 github 地址,有很简单的一句介绍:
Async await wrapper for easy error handling
这句话的意思是:
Async await 包装器,便于错误处理
等等,async 和 await 怎么捕获异常来着。
常规异常捕获
一般情况下,await 命令后面是一个 Promise 对象,返回该对象的结果。
如果我们希望捕获 Promise 中的错误, 这个时候需要将 await 放在try...catch结构里面。
同时,这样做,无论前面的 await 是否成功,都不会影响后续的功能。
功能设计
该功能设计源自源码的 examples 文件中提供的案例,我将它改成了使用 await-to-js 前的写法。
- UserModel:提供了 findById 方法,是一个 Promise,它主要做了两件事,如果 userId 存在,则返回 userObjet 对象,如果 userId 不存在,则用 reject 抛出错误。
- asyncTask: 两个参数,userId 和结果返回函数。使用async / await 处理异步操作 UserModel,将 await 放在 try...catch 结构里面。如果 UserModel 正常,则判断是否查到了 userId,如果 UserModel 阻塞,也不影响后面代码的执行。
- 调用 asyncTask 的时候,第一个参数传 null 。
``` const UserModel = { findById: userId => { return new Promise((resolve, reject) => { if (userId) { const userObjet = { id: userId, notificationsEnabled: true, };
return resolve(userObjet);
}
reject('Data is missing');
});
}, };
/* * 异步任务 / async function asyncTask(userId, cb) { try { const user = await UserModel.findById(userId); if (!(user && user.id)) return cb('No user found'); } catch () {; } return cb('前面的 await 可能失败了'); }
asyncTask(null, (err, newTask) => { console.log('fail'); console.log(err); console.log(newTask); }); ```
运行结果
因为 userId 的值为 null,所以 UserModel.findById 抛出了异常,而我在 catch 中没有做任何处理,所以后面的代码正常运行,将最后的代码处理结果抛给了 cb 函数。
所以,最终打印 cb 里的结果:
捕获异常
如果需要进行异常的捕获,在 catch 中将错误返回即可。
async function asyncTask(userId, cb) {
try {
const user = await UserModel.findById(userId);
if (!(user && user.id)) return cb('No user found');
} catch (e) {
return cb(e);
}
return cb('前面的 await 可能失败了');
}
运行结果
此时返回的报错内容为: Data is missing
契机
上面的例子中,虽然代码运行正常,也可以进行异常的捕获。
但是有个问题,如果想捕获不同场景下的异常,需要将每个场景都放到 try...catch 结构中,这样代码会显得有些冗余。
能否,将 try...catch 结构封装起来,直接返回结果?
这不,await-to-js 就诞生了么。
What:如何优雅捕获异常
开头说源码内容少,并不是说说看,它是真的少。
核心源码
源码用的 TypeScript,因为代码量不多,所以没有用过 TypeScript 的开发者,也可以很顺畅的阅读。
```
/*
* @param { Promise } promise
* @param { Object } errorExt - Additional Information you can pass to the err object
* @return { Promise }
/
export function to
return [err, undefined];
});
}
export default to; ```
不过,想看用 JavaScript 写的也不难,将项目 build 一下,就能得到。顺便在上面加了些注释:
``` /* * @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) { // =>true: 如果添加了额外的错误描述,则和错误描述合到一个空对象上 if (errorExt) { var parsedError = Object.assign({}, err, errorExt); return [parsedError, undefined]; } return [err, undefined]; }); }
export { to }; export default to; ```
这里需要注意下面几点:
- errorExt 是 Object 类型。
- Object.assign 在进行拷贝的时候,会把非 Object 类型转成 Object 类型。
``` const err1 = 'time'; const err2 = 's';
const task = Object.assign({}, err1, err2); console.log(task); // { '0': 's', '1': 'i', '2': 'm', '3': 'e' } ```
疑问
关于 errorExt 我是有疑问的,它的结果到底怎么回显?
源码中,promise 中的 reject 抛出的都是字符串。上面提到了,Object.assign 会将字符串转成 Object 类型,得到的结果不是很直观。
来个例子看看运行结果:
``` async function asyncTask(userId, cb) { let err, user, savedTask, notification; [err, user] = await to(UserModel.findById(userId), { errorExt: ' ext error' }); if (err) return cb(err);
cb(null, savedTask); } asyncTask(null, (err, newTask) => { console.log('fail'); console.log(err); console.log(newTask); }); ```
运行结果
这个结果并不直观
想要更直观的内容,只能将结果进行一次特殊处理,比如对象循环,只展示 value 值
if (err) {
let errList = Object.values(err);
let errStr = errList.join('');
return cb(errStr);
}
运行结果
这次打印的内容直观多了。
关于疑问的部分,仅个人想法,欢迎留言讨论
示例演示
还是上面的例子,这次使用 await-to-js 捕获异常。只需要一行代码,便可轻松捕获异常。
``` import to from '../../dist';
async function asyncTask(userId, cb) { let err, user, savedTask, notification; [err, user] = await to(UserModel.findById(userId)); // 直接捕获异常 if (err) return cb(err); }
asyncTask(null, (err, newTask) => { console.log('fail'); console.log(err); console.log(newTask); }); ```
运行结果
How:使用场景小结
不仅可以直接捕获异常,还可以根据数据的值进行特殊的逻辑处理,帮助完成一些复杂逻辑。
``` async function asyncTask(userId, cb) { let err, user, savedTask, notification; [err, user] = await to(UserModel.findById(userId)); if (!(user && user.id)) return cb('No user found');
cb(null, savedTask); } ```
运行结果
在上面的代码中,根据得到的 user 的值,进行判断,在实际开发中的实用性更强一些。
总结
await-to-js 源码读完,个人感觉实际用起来,代码会写的比较优雅。而且它的源码内容不多,简单易读。
简单总结一下本文收获:
1、使用 await-to-js,可以帮助改善异步捕获异常的冗余写法。
2、源码的代码量不多,可以作为工具类放到项目中使用。
3、在日常开发中,可以留意一下类似的功能改造,减少重复写法。
以上就是本次分享的内容。如果觉得有帮助,欢迎留言讨论、点赞 、收藏,持续产出技术分享。
我是 叶一一,非职业「传道授业解惑」的技术博主。「趣学前端」、「CSS畅想」系列作者。
华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
欢迎技术或非技术问题的讨论。
本文正在参加「金石计划」
- await-to-js 源码分析,体验一把捕获异常的优雅
- CSS伪类的第三集,原来伪类也可组CP
- 从:is()说起,开启CSS伪类第二集
- 一组纯CSS开发的聊天背景图,帮助避免发错消息的尴尬
- 「CSS特效」我的发呆专属,反复解锁手机屏幕
- 「技术分享」以Antd为例,快速打通UI组件开发的任督二脉
- 「功能实现」我封装了一个表单组件,感觉离财富自由又近了一步
- 「经验总结」高效开发,老代码可以这样动
- 前端开发提效小技巧之业务功能篇
- 人生有忙忙碌碌,也有诗和远方 | 2022年中总结
- 【端午节】新奇体验,我用react实现网页游戏的全过程(包括规则设计)
- 【暑假记忆】消暑神器,我用CSS复刻了一个游戏机
- 突围?我愿称之为向上的攀登者
- 【孟夏之遇】望孟夏之短夜兮,萤星相伴
- 【技术学习】SVG-边学边做
- 【TS实践】自己动手丰衣足食的TS项目开发
- 【码上掘金】通过FileReader读取Excel文件内容
- 【Taro开发】四月芳菲,Taro观赏指南
- 【Node.js】青梅煮酒,聊聊zlib压缩
- 【知识点】关于iframe跨域通信