本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
1. 引言
被这期源码阅读活动的标题所深深吸引了,主要自己看到标题时也思考了一会儿,应该来说是完全没有思路应该如何去实现;抱着满溢出来的好奇心和求知欲点开了这一期的指导文章,大致看完指导文章之后基本了解到了await-to-js的实现原理,之后进入源码解析。
2. 源码解析
官方库查看实现源码,代码位置src/await-to-js.tsgithub
/**
* @param { Promise } promise
* @param { Object= } errorExt - Additional Information you can pass to the err object
* @return { Promise }
*/
export function to<T, U = Error> (
promise: Promise<T>,
errorExt?: object
): 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;
to
整个代码比较简短,就只包含有一个to函数,下面逐一分析,因为源码使用TypeScript编写,所以说明中会包含一些类型讲解的,如果想查看JavaScript版本的代码,可将源码clone之后npm run build编译就能得到JavaScript代码
-
定义函数
to<T, U = Error> ( promise: Promise<T>, errorExt?: object ): Promise<[U, undefined] | [null, T]>;<T, U = Error>,定义函数需要捕获的用户所提供的类型,其中U固定类型为Error( promise: Promise<T>, errorExt?: object )定义函数所需要的参数,promise: Promise<T>表示需要捕获异常的异步调用,errorExt?: object可选参数,需要传递给错误的额外信息Promise<[U, undefined] | [null, T]>定义函数的返回值,一个返回[U, undefined]或[null, T]的Promise对象
-
函数体实现
.then<[null, T]>((data: T) => [null, data])返回未发生异常时异步调用的处理结果,此时错误为null.catch<[U, undefined]>((err: U) => { if (errorExt) { const parsedError = Object.assign({}, err, errorExt); return [parsedError, undefined]; } return [err, undefined]; });通过
.catch捕获到异步调用的出错信息,当有错误额外信息时通过Object.assign函数合并错误和传入的错误额外信息,否则直接返回错误信息。 -
最终通过函数
to的转化,将Promise的错误或返回结果通过返回值的方式给出,类似于go语言中的错误处理,使得代码中try...catch量减少,但await-to-js只能说是给我们提供了另外一种错误处理的思路吧(一个积极的探索),不能说一定就比try...catch好,具体使用还是需要看具体场景吧(其实无处不在的if err != nil也是go语言错误处理中被人诟病的地方)。
test
通过测试代码进一步理解
import { to } from '../src/await-to-js'
describe('Await to test', async () => {
it('should return a value when resolved', async () => {
const testInput = 41;
const promise = Promise.resolve(testInput);
const [err, data] = await to<number>(promise);
expect(err).toBeNull();
expect(data).toEqual(testInput);
});
it('should return an error when promise is rejected', async () => {
const testInput = 41;
const promise = Promise.reject('Error');
const [err, data] = await to<number>(promise);
expect(err).toEqual('Error');
expect(data).toBeUndefined();
});
it('should add external properties to the error object', async () => {
const promise = Promise.reject({ error: 'Error message' });
const [err] = await to<
string,
{ error: string; extraKey: number }
>(promise, {
extraKey: 1
});
expect(err).toBeTruthy();
expect((err as any).extraKey).toEqual(1);
expect((err as any).error).toEqual('Error message')
});
it('should receive the type of the parent if no type was passed', async () => {
let user: { name: string };
let err: Error;
[err, user] = await to(Promise.resolve({ name: '123' }));
expect(user.name).toEqual('123');
});
});
总计4个测试用例
- 正确返回结果
- 抛出异常,但未指定错误额外信息
- 抛出异常,同时指定错误额外信息
- 未传递类型
3. 总结
通过对源码await-to-js的学习,也了解到一种新的错误处理的角度,开拓了自己的思路,同时也对于await和Promise对象有了更深入的了解;代码虽然比较简短,但给了一种眼前一亮的感觉,原来代码还能这样写,这大概就是阅读源码的魅力吧。
人学始知道, 不学非自然。