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可以隔离错误,有错误隔离效果

参考资料

时间: 2024-12-29 22:47:28

JS模块间错误隔离的相关文章

js 页面间的通信

目录 一.window对象的open方法 1.window对象 2.window对象的open方法 (1)window.open(URL,name,features,replace) (2)用window.open方法实现页面通信 3.window.showModalDialog (1)window.showModalDialog (2)window.showModelessDialog (3)兼容性 4.window的height和width (1)原生js的window.innerWidth

Node.js权威指南 (10) - Node.js中的错误处理与断言处理

10.1 使用domain模块处理错误 / 272 10.1.1 domain模块概述 / 272 10.1.2 创建并使用Domain对象 / 274 10.1.3 隐式绑定与显式绑定 / 276 10.1.4 绑定回调函数与拦截回调函数 / 279 10.1.5 domain堆栈的弹出与推入 / 280 10.1.6 Domain对象的销毁 / 28610.2 Node.js中的断言处理 / 286 10.2.1 equal方法与notEqual方法 / 287 10.2.2 strictE

如何发布一个自定义Node.js模块到NPM(详细步骤)

咱们闲话不多说,直接开始! 由于我从没有使用过MAC,所以我不保证本文中介绍的操作与MAC一致. 文章开始我先假定各位已经在window全局安装了Node.js,下面开始进行详细步骤介绍: 本文本着,以极少的文字说明以及极少的代码书写为原则来给大家演示! 文章中上传的模块不具备任何意义! 一.封装node.js模块时的必须项 1.创建package.json 每一个完整封装的node模块,必须含有一个参数明确的package.json文件! 以下为package.json的最精简配置: { "n

Developer - 如何自我保证Node.js模块质量

组里正在做SaaS产品,其中一些模块(Module)是Node.js实现,这里我们主要使用Node.js实现Web Server来提供服务. 在做SaaS项目之前,组里的开发模式是传统的Deverloper + QA的模式,这是传统的协作模式,Developer负责写代码开发,当然也会有基本的自测,QA负责测试,遇到问题,提Bug给Developer去修复,Developer修复Bug后,由QA来验证并记录Bug.但这样的协作模式已不适合SaaS产品的开发,SaaS产品更新迭代快,模块众多,这就

Node.js 模块机制及常见面试问题解答

Node.js 模块机制采用了 Commonjs 规范,弥补了当前 JavaScript 开发大型应用没有标准的缺陷,类似于 Java 中的类文件,Python 中的 import 机制,Node.js 中可以通过 module.exports.require 来导出和引入一个模块. 在模块加载机制中,Node.js 采用了延迟加载的策略,只有在用到的情况下,系统模块才会被加载,加载完成后会放到 binding_cache 中. 面试指南 require的加载机制?,参考:模块加载机制 modu

Python 中模块间全局变量的使用上的注意

最近用Python写代码,需要用到模块间的全局变量. 网上四处搜索,发现普遍做法是把全局变量放到一个独立的模块中,使用时,导入此全局变量模块即可. 但是在实际使用过程中发现了些小问题:在使用如下代码导入全局变量模块时,各个模块获取到的全局变量都是初始值. from module import global_var 但是如果使用「模块名.全局变量名」来访问时,却又是正常的: import module print module.global_var 为了弄清其中的原委,我写了个测试程序来仔细查看其

js堆栈溢出错误

function prompt() { var answer = prompt("What is your name","") if (answer) { alert("Hello, " + answer + "!"); } } 提示堆栈溢出.我就纳闷了. 反复测试之后,发现原来函数名不能命名为prompt. function prompttt() { var answer = prompt("What is you

初探linux内核编程,参数传递以及模块间函数调用

一.前言                                  我们一起从3个小例子来体验一下linux内核编程.如下: 1.内核编程之hello world 2.模块参数传递 3.模块间函数调用 二.准备工作                           首先,在你的linux系统上面安装linux头文件,debian系列: 1 $:sudo apt-get install linux-headers-`uname -r` 安装后,在你的/lib/modules/目录下有你刚

js模块开发(一)

现在嵌入页面里面的javascript代码越来越复杂,于是可能依赖也越来越严重,使用别人开发的js也越来越多,于是在理想情况下,我们只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块. 于是js模块化开发就显的越来越重要了,但是,Javascript不是一种模块化编程语言,它不支持"类"(class),更遑论"模块"(module)了.(正在制定中的ECMAScript标准第六版,将正式支持"类"和"模块",但还需要很