美文网首页js css html
async/await如何优雅捕获错误是该有个结论了

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

作者: microkof | 来源:发表于2023-02-28 00:00 被阅读0次

    前言

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

    哪些阶段可以捕获错误

    • 对于Ajax请求,捕获错误分3个阶段,依次是:Ajax拦截器、业务代码捕获、全局捕获。

    • 对于来自于第三方库的Promise和非Ajax的Promise,捕获错误分2个阶段:业务代码捕获、全局捕获。

    分别解释:

    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方案,全原生代码,且无缺点。

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

    相关文章

      网友评论

        本文标题:async/await如何优雅捕获错误是该有个结论了

        本文链接:https://www.haomeiwen.com/subject/xhxmxltx.html