zoukankan      html  css  js  c++  java
  • JS模块间错误隔离

    问题背景:

    页面中有多个功能模块,怎么在一个模块出了问题之后,保证其它模块的正常工作。

    上面的差不多就是面试官的原话了,姑且称之为模块间错误隔离问题

    第一反应是动态按需加载代码,用户操作发生后再加载对应模块代码,面试官(后文简称:对面)说所有模块代码都是在页面载入时加载的,不允许动态加载。

    第二反应是error事件处理器return true,对面问确定这样做能隔离错误吗?不确定,好吧。。接着想

    第三反应是try-catch,对面问怎么个try-catch法?说用try把各个模块包裹起来啊,也可以用工厂。。哦,那你写个工厂给我看看。。然后就傻傻地写了个这:

    function getModule(type){
        switch(type){
        
        case Consts.type1 :
            return function(){
                try{
                    Modules[type]();
                }catch(err){
                    // fix
                }
            };
            break;
    
        ...
        }
    }
    

    看对面不是很满意,就又补充说也可以把case里的匿名方法提出来,作为一个包装工具,但就只能做统一错误处理,而用这个可以针对模块做不同的错误处理,各有各的好处。。。对面勉强点头

    之后对面沉默了很久,2分钟吧,有些忐忑,就弱弱地问是不是上个问题的答案不是您想要的?对面说:还行,用异常处理包起来确实可以。。

    但感觉和对面想要的答案还是有些差距,所以有了本文

    一.子问题

    从上面的面试过程能找出几个子问题:

    1. 动态按需加载能不能隔离错误?
    2. error事件处理器return true能不能隔离错误?
    3. 闭包能不能隔离错误?如果把各个模块都放在各自的闭包里,像YUI一样,有用吗?
    4. try-catch怎么用才比较好?一定要用工厂吗?

    先给出测试结果:

    1. 动态加载能隔离错误,因为在没有try-catch的情况下,错误的影响范围(作用域?)是script标签或者整个外部js文件,也就是说,如果script标签中或者外部js文件的第n行发生了错误,那么第n行后面的代码都不会再执行了。。。所以通过插入script标签来动态加载,确实能隔离错误
    2. 处理error事件不能隔离错误,让error事件处理器返回true只能抑制浏览器报错,没有什么恢复断点的作用,对程序员而言并没有实际意义
    3. 闭包不能隔离错误,但可以隔离影响,YUI的每个模块都被放在闭包中,这样可以更方便地管理作用域,避免模块间的相互影响
    4. try-catch这样用比较好:
      function getSafeFun(fun){    // 集中处理错误
          return function(){
              try{
                  fun();
              }catch(err){    
                  if (err instanceof TypeError) {
                      // 类型不匹配
                  }
                  else if (err instanceof ReferenceError) {
                      // 引用错误
                  }
                  else{
                      // ...
                  }
              }
          };
      }
      
      function getSafeFun2(fun, errHandler){  // 针对函数处理错误
          return function(){
              try{
                  fun();
              }catch(err){
                  errHandler();
              }
          };
      }
      

      上面的是基础包装工具,还可以进一步封装,添一个好用的外观(Facade):

      // 配置数据
      var Modules = {};
      Modules.mod1 = {
          desc : "模块1",
          method : errorFun
      };
      Modules.mod2 = {
          desc : "模块2",
          method : fun
      };
      
      /*
       * 统一模块调用接口
       */
      function use(moduleName, errHandler){
          if (typeof errHandler === "function") {
              getSafeFun2(Modules[moduleName].method, errHandler)();
          }
          else {
              getSafeFun(Modules[moduleName].method)();
          }
      }
      

      直接用use传入模块名和可选的错误处理器就可以隔离错误了,感觉好多了

      不需要工厂,工厂是根据给定的参数返回对应类型的东西,而我们所做的不过是用try包裹了一下而已,和工厂没多大关系,感觉和装饰、外观的关系更大一点。。当然,重要的是好用,而不是一定要用什么模式

    二.测试验证

    1.动态按需加载能不能隔离错误?

    测试代码:

    <script type="text/javascript">
        script1
        alert(1);
    </script>
    
    <script type="text/javascript">
        alert(2);
    </script>
    

    运行结果:2,script标签能够隔离错误,所以动态加载也能隔离错误

    2.error事件处理器return true能不能隔离错误?

    测试代码:(在head里的script标签中插入如下代码)

    window.onerror = function(e){
        return true;    // 不报错
    }
    

    运行结果:不报错,也不会alert 1,对程序员而言没什么作用,不能隔离错误

    3.闭包能不能隔离错误?如果把各个模块都放在各自的闭包里,像YUI一样,有用吗?

    测试代码:

    // 闭包1
    (function(){
        closure
        alert(1);
    })();
    // 闭包2,无法执行,因为闭包1出错了
    (function(){
        alert(2);
    })();
    

    运行结果:没有alert任何东西,只要闭包1和2在同一个script标签或者同一个外部js文件中,闭包2都会因为闭包1出错而无法执行,所以闭包不能隔离错误

    4.try-catch怎么用才比较好?

     当然不能强制要求所有编码人员都在调用模块的时候用try包裹,我们至少得有一个包装工具,像这样的:

    function getSafeFun(fun){   // 集中处理错误
        return function(){
            try{
                fun();
            }catch(err){    
                if (err instanceof TypeError) {
                    // 类型不匹配
                }
                else if (err instanceof ReferenceError) {
                    // 引用错误
                }
                else{
                    // ...
                }
            }
        };
    }
    
    function getSafeFun2(fun, errHandler){  // 针对函数处理错误
        return function(){
            try{
                fun();
            }catch(err){
                errHandler();
            }
        };
    }
    
    /* 测试 */
    function errorFun(){
        errorFunction
        alert(1);
    }
    
    function fun(){
        alert(2);
    }
    
    getSafeFun(errorFun)();
    getSafeFun(fun)();
    

    现在有了getSafeFun()和getSafeFun2(),可以少写一点try了,但还是得要求所有编码人员自己看情况调用才能隔离错误,还是不科学,应该再添点什么

    // 配置数据
    var Modules = {};
    Modules.mod1 = {
        desc : "模块1",
        method : errorFun
    };
    Modules.mod2 = {
        desc : "模块2",
        method : fun
    };
    
    /*
     * 统一模块调用接口
     */
    function use(moduleName, errHandler){
        if (typeof errHandler === "function") {
            getSafeFun2(Modules[moduleName].method, errHandler)();
        }
        else {
            getSafeFun(Modules[moduleName].method)();
        }
    }
    
    /* 测试 */
    use("mod1");
    use("mod1", function(){
        alert("fix");
    });
    use("mod2");
    

    现在就比较人性化了,只留一个入口,只需要告诉编码人员以前的模块调用方式过时了,现在的新API是use即可

    三.结论

    抛开问题本身,上面的所有测试结果可以归纳如下:

    1. 一个script标签中的代码发生错误,不会导致页面其它script标签内代码不执行
    2. window.onerror事件处理器中return true只能让浏览器不报错,而后面的代码不会再执行了
    3. 闭包对模块间错误隔离无益,但可以隔离模块间影响
    4. try-catch可以隔离错误,有错误隔离效果

    参考资料

  • 相关阅读:
    Mybatis应用
    MyBatis
    jQuery中.bind() .live() .delegate() .on()的区别
    SpringMVC (八)SpringMVC返回值类型
    SpringMVC(七)参数的自动装配和路径变量
    SpringMVC (六)注解式开发
    SpringMVC (五)视图解析器
    SpringMVC (四)MultiActionController
    SourceTree的使用
    Idea中使用git
  • 原文地址:https://www.cnblogs.com/ayqy/p/4466553.html
Copyright © 2011-2022 走看看