【若川视野 x 源码共读】第21期 | await-to-js

157 阅读2分钟

本文参加了由公众号@若川视野发起的每周源码共读活动,点击了解详情一起参与。

目标

学习await-to-js用法及其原理

解决什么?

一般情况下处理async await 产生的异常,需要包裹一层try catch。await-to-js通过简单封装一层来处理造成异常。

如何使用?

安装

npm install await-to-js

例子

import { to } from 'await-to-js';

function findUserById(userId = 1) {
  return new Promise((resolve, reject) => {
    if (userId == -1) {
      reject(new Error('userId 不能为 -1'));
    } else {
      resolve({
        id: userId,
        msg: '找到信息'
      })
    }  
  });
}

async function test() {
  // 通过 to 包裹一层,返回形式为[err, data]
  let [err1, data1] = await to(findUserById(2));
  let [err2, data2] = await to(findUserById(-1));

  console.log(err1, data1);
  console.log(err2, data2);
}

test();

/*
输出结果:
null { id: 2, msg: '找到信息' }
Error: userId 不能为 -1
    at file:///C:/Users/Administrator/Desktop/core-study/demo/test-await-to.js:6:14
    at new Promise (<anonymous>)
    at findUserById (file:///C:/Users/Administrator/Desktop/core-study/demo/test-await-to.js:4:10)
    at test (file:///C:/Users/Administrator/Desktop/core-study/demo/test-await-to.js:18:32) undefined

*/

怎么实现?

了解了怎么使用之后,我们来看看它内部是怎么实现的:

/**
 * @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]> {
  // 通过传进来的promise函数,通过then catch 来进行一些数据处理
  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;

通过上面可以看到,内部其实就是多进行了数据封装而已。这边写下js形式~

export function to (promise,errorExt){
  // 通过传进来的promise函数,通过then catch 来进行一些数据处理
  return promise
    .then((data) => [null, data])
    .catch((err) => {
      if (errorExt) {
        // 这里进行错误信息合并(包括传进来的参数)
        const parsedError = Object.assign({}, err, errorExt);
        return [parsedError, undefined];
      }
      return [err, undefined];
    });
}

export default to;

测试用例

通过test/await-to-js.test.ts可以看出,await-to-js包中采用的测试框架是jest

从测试用例可以看出主要对参数的类型进行测试,主要是对四种情况进行测试:

  • Promise.resolve
    • 带参
    • 不带参
  • Promise.reject
    • 带参
    • 不带参
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);
    // Promise.resovle 无参测试
    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');
    // Promise.reject 无参测试
    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' });
    // Promise.reject传参测试
    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;
    // Promise.resolve 传参测试
    [err, user] = await to(Promise.resolve({ name: '123' }));

    expect(user.name).toEqual('123');
  });
});

总结

从 await-to-js 中可以学到的几点:

  • async await中的异常处理方式,可通过try-catch包裹
  • 有效的简单封装使得代码易于维护
  • 了解到新的测试框架jest