js css html

async/await如何优雅捕获错误是该有个结论了

2023-02-28  本文已影响0人  microkof

前言

async/await如何优雅捕获错误在业界一直存在争论,事实上并没有一个完全得劲的法子,就看相对来讲谁更得劲,而且自己用起来最顺手,那么就用哪一种。今天我们分析一下。

哪些阶段可以捕获错误

分别解释:

  1. Ajax拦截器是第一层捕获,相当于先行捕获,当然仅限于Ajax请求。拦截器可以拦截掉一批错误,当然必须约定好错误特征,然后也要放行一部分错误,交给业务代码捕获。

  2. 业务代码捕获是本文讨论的重点。

  3. 全局捕获是最后一层捕获,相当于兜底捕获。用法是window.addEventListener('unhandledrejection', fn)。全局捕获主要用于“阻止控制台打印错误”和“统一处理错误”,使用event.preventDefault();可以阻止报错信息出现在控制台,也就是让控制台干净一点,所谓“统一处理错误”就是某些后续处理如果全局统一,则可以一齐处理,比如可以捕获错误日志,上传到服务器。一个成熟的项目是一定应该有一个全局捕获处理的,这个没必要犟。

总之,先行捕获和兜底捕获就处理了很多错误,也就是说业务代码中就不必再写相关捕获代码了,下文集中讨论在业务代码中如何优雅捕获错误。

方案一:try...catch...

ES官方的解决方案,当然也是大家最看不上的方案。

优点

浏览器直接支持,且代码一目了然。

缺点

  1. 原本一行 await 让try...catch...搞成了N行,而且2套大括号,相当于2个块级作用域,真的不优雅。

  2. 接收数据的变量必须在try外部声明,否则没法传递到后续代码。(除非用已经过时的var,然而用var会引起不必要的歧义,所以你根本不应使用var。)

方案二:await-to-js库

伪代码:

var [error, result] = await to(Promise对象);
if (error) {
    alert("xxxxx出错");
    return;
}
var [error, result] = await to(Promise对象);
if (error) {
    alert("ooooo出错");
    return;
}

优点

  1. try...catch是2套大括号,现在await-to-js只需要关注error,所以await-to-js是一套if大括号,而且if可以省掉大括号。

  2. 不存在深层块级作用域,result变量可以轻松传递给后续代码。

缺点

  1. 必须引入一个库,且必须用to()包裹Promise对象。

  2. 将错误和结果包装进一个数组,这种做法有人说觉得很古怪。

方案三:await Promise对象.catch(e=>{...})

这个方案是给Promise对象接一个catch小尾巴。为了完整演示,我加了全局捕获:

      window.addEventListener("unhandledrejection", (e) => {
        e.preventDefault();
        console.log("捕获了", e); // 会执行
      });

      function p() {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            reject(123);
          }, 1000);
        });
      }

      const handle = async () => {
        const result = await p().catch((err) => {
          alert(err);
          return Promise.reject(err);
        });
        console.log(result); // 不会执行
      };
      handle();

要点是catch函数里必须return一个reject的Promise,这样才能阻止代码继续执行。当然,也可以throw new Error(err),都行。

优点:

  1. 全原生。

  2. 不存在变量作用域烦恼。

  3. 行数比await-to-js还少一行(如果if不用语法糖的话)。

缺点:

  1. 必须用全局捕获才能阻止错误出现在控制台,但是我说了,全局捕获是项目标配,所以这个缺点不叫缺点。

  2. 必须继续抛出错误,不过由于await-to-js也至少要写return false,都要占行数,所以平手。

不是方案纯属搞笑:用babel插件给await包裹try...catch...

这类插件还不止一个,随便说一个:https://www.npmjs.com/package/babel-plugin-await-add-trycatch

为什么我说这些插件纯属搞笑呢?

你在编译后给await包裹try...catch...有个卵锤子用?你也不能自定义catch分支的代码,所以唯一的卵用就是不让错误暴露在控制台,针对这点我只想说,插件的开发者们就不知道有全局捕获这个玩意么?

总结

不言而喻,高下立判,最优解就是方案三,也就是catch方案,全原生代码,且无缺点。

如果您还有不同观点,请指出。

上一篇下一篇

猜你喜欢

热点阅读