[源码阅读]await-to-js:优雅解决错误处理

138 阅读5分钟

前言

持续创作,加速成长!这是我参与「掘金日新计划 · 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)
    

    这里,我们明确指出,Tnumber类型的,并给参数也指定类型

  • 进行类型推断

    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>,我觉得,意思就是,这个函数里面会用到这两种类型,TU,并且U初始值为Error
  • to函数有两个参数,一个是Promise,一个是errorExt
    • Promise:类型是Promise<T>,我的理解是,他是一个Promise实例,它里面执行的回调函数,返回的类型是T
    • errorExt:类型是一个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写的很少