- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第21期,链接:www.yuque.com/ruochuan12/…。
今天分析的源码是 await-to-js, 该包的作者受困于对 ES7中的 await 的错误的捕获还需要使用 try/catch的方式, 因此借鉴于 golang 里的错误处理的简洁模式, 开发了这个包.
本包的使用方式如下
async function asyncTaskWithCb(cb) {
let err, user, savedTask, notification;
[ err, user ] = await to(UserModel.findById(1));
if(!user) return cb('No user found');
[ err, savedTask ] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
if(err) return cb('Error occurred while saving task');
if(user.notificationsEnabled) {
[ err ] = await to(NotificationService.sendNotification(user.id, 'Task Created'));
if(err) return cb('Error while sending notification');
}
if(savedTask.assignedUser.id !== user.id) {
[ err, notification ] = await to(NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'));
if(err) return cb('Error while sending notification');
}
cb(null, savedTask);
}
当然了, 作者也不是建议在所有的地方都使用这个包 如果比较简单的话直接使用 try/cathc 也是可以的.
源码
全部代码非常简短, 本质上是将入参 promise 的结果进行了二次处理, 通过元组类型统一了返回格式:
如果成功则返回 [null, T]; 如果失败则返回 [err, undefined]. 同样还提供了第二个参数 errorExt来实现自定义错误
// 定义了两个泛型 T, U(默认为 Error类型)
export function to<T, U = Error> (
promise: Promise<T>,
errorExt?: object
// 元组类型+联合类型 定义了to的返回值类型
): Promise<[U, undefined] | [null, T]> {
return promise
.then<[null, T]>((data: T) => [null, data])
.catch<[U, undefined]>((err: U) => {
if (errorExt) {
const parsedError = Object.assign({}, err, errorExt);
return [parsedError, undefined];
}
return [err, undefined];
});
}
export default to;
疑惑之处
通过对源码的阅读以及测试, 我发现一个问题, 对于自定义错误或者用作者的话是 Additional Information you can pass to the err object , 那么就我的理解, 我传入 errorExt?: object的时候会用对象的形式 {'msg': 'hhhh'}
if (errorExt) {
const parsedError = Object.assign({}, err, errorExt);
return [parsedError, undefined];
}
在源码中的例子中 examples\example-1.js, 它使用的是 reject('Data is missing') 的方式, 所以源码中 U 的类型为 string, 而打印合并后的对象 parsedError 的值如下, 而这样的值 并不是我们想要的.
{
'0': 'h',
'1': 'h',
'2': 'h',
'3': 'h',
'4': ' ',
'5': 'i',
'6': 's',
'7': ' ',
'8': 'm',
'9': 'i',
'10': 's',
'11': 's',
'12': 'i',
'13': 'n',
'14': 'g'
}
如果改成使用 Error 的形式返回, 即 reject(Error("Data is missing")), 那么实际上 const parsedError = Object.assign({}, err, errorExt); 也是无法合并的, 实际测试结果如下
e = Error('error')
Object.assign({}, e)
output: {}
Object.assign({},e ,{msg:'hhhh'})
output: {msg: 'hhhh'}
因此我觉得源码这里的处理是有问题的.
为此我有两种想法, 第一种是针对 err 的类型做出判断然后再处理, 不过这样的方式比较麻烦
第二种是修改了一下返回类型, U => {err: U}, 即将err 包裹成对象
export function to<T, U = Error>(
promise: Promise<T>,
errorExt?: object
): Promise<[{ err: U }, undefined] | [null, T]> {
return promise
.then<[null, T]>((data: T) => [null, data])
.catch<[{ err: U }, undefined]>((err: U) => {
if (errorExt) {
const parsedError = Object.assign({}, { err }, errorExt);
return [parsedError, undefined];
}
return [{ err }, undefined];
});
}
export default to;
我个人认为第二种方法比较合适, 不知道大家有什么想法, 欢迎交流~