前言
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
【若川视野 x 源码共读】第21期 | await-to-js 如何优雅的捕获 await 的错误 点击了解本期详情一起参与。
源码
地址
源码地址:scopsy/await-to-js: Async await wrapper for easy error handling without try-catch (github.com)
意外之事
我当时clone下来,并且直接npm i进行安装以后,出现了一些报错,具体报错是处在types.d.ts文件,但是这明明是一个类型定义文件,我并没有太多关注,因为我们发现它npm run test并不会造成影响。🤷♀️
但是,今天我朋友也clone了·一下,并没有任何报错(😲),我又仔细查看了一下报错,定位到了微软啥的,也是一个类型文件,百思不得其解(😕),之后就一鼓作气将vscode卸载重装了一次,好吧,没有卵用。😡
后续我突然想到了一个库node_type这种,他里面有node的一些类型定义,直接安装npm install @types/node --save-dev,很好,报错完美解决。
作用
官方:Async await wrapper for easy handling、
可以仔细参考一下他们的官方博客:How to write async await without try-catch blocks in Javascript (grossman.io)
简单来说:他就是用来处理我们请求的一些错误处理的
在js中,我们抛出错误,但是假如我们没有进行捕获,那么我们会将error一直传下去,我的理解是,只要这个函数有问题(错误没有捕获),那么他的调用者也会有这个问题,就会类似于冒泡一样,但是如果我们使用了这个库,我们的错误就可以直接被返回出来[errText,undefined]的形式
源码分析
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;
你可能会对<T,U>,这种感到奇怪,那么我们先来简单聊一下这个它
泛型
简单来说:泛型就是来约束类型的,也就是泛型和我们理解中的js类型一样,他也是一种类型
举一个例子:
你现在有一个函数,规定参数是number,并且返回值也是number
你一定可以写出来:
function fn1(a:number):number{}
那么我现在规定,参数是string,返回值也是string
function fn1(a:string):string{}
前面这两种我们都可以轻松的解决,那么我现在说
我要求,参数类型和返回值类型保持一致
那么我们就可以使用泛型
function fn1<T>(a:T):T{}
在这里面,T就是一个泛型变量,只要我们使用了T,那么我们就需要知道,T这个类型,就是一个包含所有类型的类型
调用函数,一般我有两种方式
-
传入所有的参数
let output = fn1<number>(1)这里,我们明确指出,
T是number类型的,并给参数也指定类型 -
进行类型推断
let output = fn1(1)这里,我们会使用类型推断,因为我们的参数是
number类型,所以我们的T就可以知道number类型的
泛型类
其实就是在类名的后面增加一个
<>
class Generate<T>{
zeroValue:T;
add:(x:T,y:T)=>T;
}
泛型类指的都是实例部分的类型,所以在类的静态属性中是不可以出现泛型类型的
泛型约束
前面我们提到过了,使用泛型,那么你就需要考虑到你这个泛型其实是
js所有类型的一种类型,所以这时候你可能会需要约束一下你的泛型
举例:你希望获取参数的个数length
- 那么如果你直接使用(泛型)
T,那么是会报错的,因为Number类型不具有这个属性 - 这时候我们就需要约束条件
interface Lengthwise{
length:number
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
- 在这个里面我们就实现了对
T进行了约束,我们希望T这个类型满足LengthWise接口
参考文章:泛型(generic) - TypeScript 中文手册 (bootcss.com)
代码分析
那么现在我来聊一下这段代码
- 首先,这是一个泛型,
<T,U=Error>,我觉得,意思就是,这个函数里面会用到这两种类型,T和U,并且U初始值为Error to函数有两个参数,一个是Promise,一个是errorExtPromise:类型是Promise<T>,我的理解是,他是一个Promise实例,它里面执行的回调函数,返回的类型是TerrorExt:类型是一个object
- 现在我们看一下这个
to函数的返回值Promise<[U, undefined] | [null, T]>,那么现在我们可以推断,其实上是一个Promise实例,因为js会将函数返回值变为Promise,所以呢,也就是它返回的值是[U, undefined]或者[null, T]。 - 继续往下我们看到,事实确实是这样,它返回的是一个
Promise实例- 如果
Promise状态变为了fullfilled,那么执行then这个函数- 回调函数是一个
泛型函数,这个回调函数的返回值是[null,T] - 并且我们规定了,
then的回调函数的参数类型必须是T类型
- 回调函数是一个
- 如果变为
reject,那么我们需要捕获错误,执行catch里面的回调函数- 如果我们含有
errorExt,我理解的errorExt是一些错误信息- 如果有错误信息,那么我们就使用
Object.assign(),将新的错误信息覆盖到原来的错误信息上,返回[parsedError, undefined]
- 如果有错误信息,那么我们就使用
- 如果没有错误信息,那么直接将原来的错误信息返回
[err, undefined]
- 如果我们含有
- 如果
使用
现在来查看一下这个测试代码
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);
});
T:number类型- 这边是
resolve(),所以Promise状态变为了fullfilled - 那么会执行,然后返回值
[null,data] - 对结果进行解构赋值
- 然后判断结果
总结
首先看这个代码,确实花费了我一点力气,因为我之前并没有很好的了解过Promise,对泛型也没有很好的了解,现在我对这些也没有很迷惑了
但是我其实对tru..catch写的很少