浅谈JS中的回调,事件,异步回调,和错误捕捉

2012-09-19 10:42

浅谈JS中的回调,事件,异步回调,和错误捕捉

by admin

at 2012-09-19 02:42:39

original http://item.feedsky.com/~feedsky/helloJavaScript/~8514355/703064731/6618683/1/item.html

浅谈JS中的回调,异步回调,和错误捕捉。

关于NodeJS的种种就不介绍和吐槽了,我的了解也不是非常多,正在学习中。

这里就说说JS中的回调和错误捕捉。

回调

先从例子开始吧。

/////////////////////
var example_2=function(callback){
    callback();
}
try{
    example_2(function(){
        throw new Error("callback function's outer error")
    });
}catch(e){
    console.log(e)
}

上图演示了基本的回调方法和外层错误捕捉,所谓回调,其实就是在方法的参数内传入另一个方法,在执行到一定时间的时候,执行回调方法。

上面演示的回调方法里有一句throw error,会抛出一个异常,然后我们用try catch 在外层进行捕捉,最后可以发现错误被捕捉,不会导致程序出错,并输出错误信息。

Error:callback function's outer error

事件

事件其实就是一种被打扁的回调,从语义上来理解,其实也是一种回调,代码的执行都是同步阻塞。你绑定一个事件,其实就是增加了一个回调,到了要调用的时候会去调用。

事件的好处是可以把回调的代码打扁平。(其实回调也可以?)

例如:

var test=new ***()
test.on("init",function(){
   this.fire("render");
});
test.on("render",function(){
   //....do other things
});

类似链式操作,但是操作仍然是串行的。

回调平铺:

**.proptype.init=function(callback){
   callback.call(this);
}
var test=new **();
var render_callback=function(){
    //....
}
var init_callback=function(){
    this.render(render_callback);
}
test.init(init_callback)

看起来好像完全不是平铺,仍然是嵌套,只是改了个写法而已,其实基于事件的实现之所以看不到这种过程,是因为都封装进了事件处理器。其实从原理上来说都是一个鸟样。

总的来说,基于事件的设计可以承载更多状态而不会显得混乱,代码更扁平(更多处理是放在事件处理器中)。而基于回调的设计,状态简单,大概只有成功失败两种状态,一个回调即可搞定,在参数中体现失败信息即可。

所以从根本上来说,二者并无差。特别是从错误捕捉方面来说。

看个真实可运行的例子吧,用的是NodeJS自带的事件处理器。

var util=require('util')
util.mix=function(target,source){
    for(var i in source){
        target[i]=source[i]
    }
}
var events=require("events")
var example_10=function(){
    events.EventEmitter.call(this);
}
util.inherits(example_10, events.EventEmitter);
util.mix(example_10.prototype, {
    init:function(){
        this.emit("init")
    }
});
var ex=new example_10();
ex.on("init",function(){
    throw new Error("event callback error")
})
try{
    ex.init();
}catch(e){
    console.log(e)
}

执行结果:

[Error:event callback error] //正常捕捉到错误,跟回调一个结果。

异步

很多对js不了解的同学容易把这些概念搞混,异步跟回调还有事件,其实并木有什么本质联系。

在JS中的异步,主要是IO的异步,不管是NodeJS还是浏览器端JS。

浏览器端JS主要的异步行为是远程请求,workers,socket等,NodeJS中在此基础上增加一些例如对本地文件的操作,还有操作数据库(其实也是socket)等。

这些操作中,大部分都设计成事件机制的,但像对本地文件的操作,和某些框架的封装(例如mysql)因为状态大多只有两个,都被设计成基于回调的。

不过上面讨论过了,不管是事件还是回调,原理都一样。所以这里只讨论回调。

看个代码(NodeJS):

var Client = require('mysql').Client;
var client = new Client();
client.user ="root";
client.password ="";
client.query('USE test');
try{
    client.query(
        'SELECT * FROM t where id=1',
        function(err, results, fields) {
          throw new Error("asyn callback function's error")
        });
}catch(e){
    console.log(e)
}

运行结果是神马呢?呵呵,这个应用会崩溃,提示throw error导致应用出错,也就是说try catch 木有捕捉到错误。

同样是回调,为啥捕捉不到捏,这就是异步回调了,JS中异步回调,因为真正的异步,已经跳出当前代码执行的顺序了,回调是由异步线程来负责监视和执行的。等回调被触发的时候,已经脱离了之前代码的trycatch的作用范围。深层次的机理,有兴趣可以去研究,这里说的都是表象。

可见,异步回调的代码必须在里面做捕捉,外面的捕捉捕捉不到异步回调代码里的报错。

var Client = require('mysql').Client;
var client = new Client();
client.user ="root";
client.password ="";
client.query('USE test');
client.query(
        'SELECT * FROM t where id=1',
        function(err, results, fields) {
          try{
          throw new Error("asyn callback function's error")
          }catch(e){
 
          }
    });

其他的异步回调的处理也类似。

其他

NodeJS比较让人烦的地方 就是回调方法的嵌套,很容易让代码变得层次很深,同时异步回调的错误又不能在外层捕捉,需要在每次回调的时候做处理,二者揉到一起,代码就更纷繁乱套了。

一般的解决方案就是把代码用事件平铺,可以用NodeJS自带的事件处理器平铺,也可以用第三方的库来做事件处理器,例如eventproxy之类的。

还有一些其他情况的错误捕捉,也许我们也该一起玩玩:

/////////
var example_3=function(callback){
    setTimeout(function(){
        callback();
    },100)
}
try{
    example_3(function(){
         throw new Error("callback function's settimeout error")
        });
}catch(e){
    console.log(e)
}

setTimeout也是真异步,定时由单独的线程维护,所以处理起来也算是异步的,上述的结果跟异步回调的结果一样,需要内部捕捉错误。

var example_3=function(callback){
    eval("(callback())");
}
try{
    example_3(function(){
        throw new Error("callback function's eval error")
    });
}catch(e){
    console.log(e)
}

eval呢,上述的运行结果是正常的,表明其处理是同步的。具体细节就不深入了,对这些了解不多。

还有其他情况可以自己去实验一下,加深一下对try catch的理解。

错误处理在前端来说,重要性并不突出,因为前端可以约定限制,不需要考虑太多情况,但是后端因为各种依赖,很容易出现异常,需要在一些关键地方做一下处理,而且调试时候也更依赖throw error等工具,重要性不言而喻,所以理解错误捕捉在不同的情况下是如何工作滴是很重要滴。

thanks for reading。