【若川视野 x 源码共读】第21期 await-to-js 如何优雅的捕获 await 的错误

96 阅读3分钟

如何优雅的捕获 await 的错误

一. 前言

场景: 调用a接口获取到返回的成功数据resulta 使用resulta才能去调用b接口 调用b接口获取到返回的成功数据resultb 使用resultb去调用c接口

二. 不使用await-to-js库

function funLogin() {
  return new Promise((resolve, reject) => {
    console.log('调用成功-登录成功');
    // resolve(`1:`)
    reject('error-test')
  })
}
function funUserId(params) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('调用成功-返回人员id');
      resolve(`2:${params}`)
    }, 1000);
  })
}
function funUserInfo(params) {
  return new Promise((resolve, reject) => {
    console.log('调用成功-返回人员的信息');
    resolve(`3:${params}`)
  })
}

不使用await-to-js

async function test() {
  let resultLogin, resultUserId, result
  try {
    resultLogin = await funLogin()
    console.log(resultLogin);
  } catch (error) {
    console.log('funLogin error:',error);
  }
  if (resultLogin) {
    try {
      resultUserId = await funUserId(resultLogin)
      // console.log(resultUserId);
    } catch (error) {
      console.log(error);
    }
    if (resultUserId) {
      try {
        result = await funUserInfo(resultUserId)
        // console.log(result);
      } catch (error) {
        console.log(error);
      }
    }
  }
}
test()

三. 为啥要出现这个库呢

我觉得是为了让代码更加优雅 下面是我对官方文档的解读

四. 进入源码

默认给的是ts版本

/**
 * @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;

打包以后可以查看它的js版本

function to(promise, errExt) {
  return promise.then(res => {
    return [null,res]
  }).catch(err => {
    if (errExt) {
      const errObj = Object.assign({},err,errExt)
      return [errObj, undefined]
    }else {
      return [err, undefined]
    }
  })
}

源码解析 传入一个包含reject或者resolve状态的promise或者说errExt(额外的错误信息) 如果promise状态为成功,那么返回一个数组,数组的第一个元素为null、数组的第二个元素为res 如果promise状态为失败,那么先判断是否传入了errExt 如果传入了errExt 那么将err与errExt整合成errObj,然后返回一个数组,数组的第一个元素为errObj、数组的第二个元素为undefined 如果没有传入errExt,那么直接返回一个数组,数组的第一个元素为err、数组的第二个元素为undefined

使用await-to-js 在浏览器中测试

async function test() {
  const [err1,resultLogin] = await to(funLogin())
  if (err1) {throw new Error(err1)}
  const [err2,resultUserId] = await to(funUserId(resultLogin))
  if (err2) {throw new Error(err2)}
  const [err3,res] = await to(funUserId(resultUserId))
  if (err3) {throw new Error(err3)}
}
test()
//调用成功-登录成功
//  1.html:115 Uncaught (in promise) Error: error-test
//  at test (1.html:115:20)

代码精简了很多

跑一跑官方测试用例 这里是测试ts版本的
import { to } from "../src/await-to-js";

describe("Await to test", async () => {
  // 测试 成功返回结果  return <null,number>
  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 () => {
    // 测试 失败处理结果 return <Error,undefined>
    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 () => {
    // 测试 失败处理结果  return 自定义类型的error
    const promise = Promise.reject({ error: "Error message" });

    const [err] = await to<string, { error: string; extraKey: number }>(promise, {
      extraKey: 1,
    });

    expect(err).toBeTruthy(); // err是否为ture 这里对于单元测试 toBe(xx) 、totoBeTruthy()、toBeTrue()这三者的区别联系 楼主就没有深究了
    expect((err as any).extraKey).toEqual(1); // err的extraKey是否为1
    expect((err as any).error).toEqual("Error message"); // err的extraKey是否为"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");
  });
});
另外想要输出了一篇Generator 函数 async、await函数的文章 主要是async的实现、co