Skip to content

async/await 怎么进行错误处理?

Posted on:2024年8月10日 at 17:06

一般情况下 async/await 在错误处理方面,主要使用 try/catch,像这样

const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("fetch data is me");
    }, 1000);
  });
};

(async () => {
  try {
    const data = await fetchData();
    console.log("data is ->", data);
  } catch (err) {
    console.log("err is ->", err);
  }
})();

这么看,感觉倒是没什么问题,如果是这样呢?有多个异步操作,需要对每个异步返回的 error 错误状态进行不同的处理,以下是示例代码

const fetchDataA = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("fetch data is A");
    }, 1000);
  });
};

const fetchDataB = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("fetch data is B");
    }, 1000);
  });
};

const fetchDataC = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("fetch data is C");
    }, 1000);
  });
};

(async () => {
  try {
    const dataA = await fetchDataA();
    console.log("dataA is ->", dataA);
  } catch (err) {
    console.log("err is ->", err);
  }

  try {
    const dataB = await fetchDataB();
    console.log("dataB is ->", dataB);
  } catch (err) {
    console.log("err is ->", err);
  }

  try {
    const dataC = await fetchDataC();
    console.log("dataC is ->", dataC);
  } catch (err) {
    console.log("err is ->", err);
  }
})();

这样写代码里充斥着 try/catch,有代码洁癖的你能忍受的了吗?这时可能会想到只用一个 try/catch。

// ... 这里 fetch 函数省略

(async () => {
  try {
    const dataA = await fetchDataA();
    console.log("dataA is ->", dataA);
    const dataB = await fetchDataB();
    console.log("dataB is ->", dataB);
    const dataC = await fetchDataC();
    console.log("dataC is ->", dataC);
  } catch (err) {
    console.log("err is ->", err);
    // 难道要定义 err 类型,然后判断吗??
    /**
     * if (err.type === 'dataA') {
     *  console.log('dataA err is', err)
     * }
     * ......
     * */
  }
})();

如果是这样写只会增加编码的复杂度,而且要多写代码,这个时候就应该想想怎么优雅的解决,async/await 本质就是 promise 的语法糖,既然是 promise 那么就可以使用 then 函数了

(async () => {
  const fetchData = () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("fetch data is me");
      }, 1000);
    });
  };

  const data = await fetchData()
    .then((data) => data)
    .catch((err) => err);
  console.log(data);
})();

在上面写法中,如果 fetchData 返回 resolve 正确结果时,data 是我们要的结果,如果是 reject 了,发生错误了,那么 data 是错误结果,这显然是行不通的,再对其完善。

(async () => {
  const fetchData = () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("fetch data is me");
      }, 1000);
    });
  };

  const [err, data] = await fetchData()
    .then((data) => [null, data])
    .catch((err) => [err, null]);
  console.log("err", err);
  console.log("data", data);
  // err null
  // data fetch data is me
})();

这样是不是好很多了呢,但是问题又来了,不能每个 await 都写这么长,写着也不方便也不优雅,再优化一下

(async () => {
  const fetchData = () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("fetch data is me");
      }, 1000);
    });
  };

  // 抽离成公共方法
  const awaitWrap = (promise) => {
    return promise.then((data) => [null, data]).catch((err) => [err, null]);
  };

  const [err, data] = await awaitWrap(fetchData());
  console.log("err", err);
  console.log("data", data);
  // err null
  // data fetch data is me
})();

将对 await 处理的方法抽离成公共的方法,在使用 await 调用 awaitWrap 这样的方法是不是更优雅了呢。如果使用 typescript 实现大概是这个样子

function awaitWrap<T, U = any>(
  promise: Promise<T>,
): Promise<[U | null, T | null]> {
  return promise
    .then<[null, T]>((data: T) => [null, data])
    .catch<[U, null]>((err) => [err, null]);
}
原文转自:https://fe.ecool.fun/topic/deb034d1-2a96-45b4-8576-132f2af9c797