javascript 异步编程2

阅读更多

好像有这么一句名言——"每一个优雅的接口,背后都有一个龌龊的实现"。最明显的例子,jQuery。之所以弄得这么复杂,因为它本来就是那复杂。 虽然有些实现相对简明些,那是它们的兼容程度去不了那个地步。当然,世上总有例外,比如mootools,但暴露到我们眼前的接口,又不知到底是那个父类 的东西,结构清晰但不明撩。我之所以说这样的话,因为异步列队真的很复杂,但我会尽可能让API简单易用。无new实例化,不区分实例与类方法,链式,等 时髦的东西都用上。下面先奉上源码:

;( function (){
     var dom = this .dom = this .dom || {
         mix : function (target, source ,override) {
             var i, ride = (override === void 0) || override;
             for (i in source) {
                 if (ride || !(i in target)) {
                     target[i] = source[i];
                 }
             }
             return target;
         }
     }
     //////////////////////////////////////////////////////////////////////
     //=======================异步列队模块===================================
     var Deferred = dom.Deferred = function (fn) {
         return this instanceof Deferred ? this .init(fn) : new Deferred(fn)
     }
     var A_slice = Array.prototype.slice;
     dom.mix(Deferred, {
         get: function (obj){ //确保this为Deferred实例
             return   obj instanceof Deferred ? obj : new Deferred
         },
         ok : function (r) { //传递器
             return r
         },
         ng : function (e) { //传递器
             throw   e
         }
     });
     Deferred.prototype = {
         init: function (fn){ //初始化,建立两个列队
             this ._firing = [];
             this ._fired = [];
             if ( typeof fn === "function" )
                 return this .then(fn)
             return this ;
         },
         _add: function (okng,fn){
             var obj = {
                 ok:Deferred.ok,
                 ng:Deferred.ng,
                 arr:[]
             }
             if ( typeof fn === "function" )
                 obj[okng] = fn;
             this ._firing.push(obj);
             return this ;
         },
         then: function (fn){ //_add的包装方法1,用于添加正向回调
             return   Deferred.get( this )._add( "ok" ,fn)
         },
         once: function (fn){ //_add的包装方法2,用于添加负向回调
             return   Deferred.get( this )._add( "ng" ,fn)
         },
         wait: function (timeout){
             var self = Deferred.get( this );
             self._firing.push(~~timeout)
             return self
         },
         _fire: function (okng,args,result){
             var type = "ok" ,
             obj = this ._firing.shift();
             if (obj){
                 this ._fired.push(obj); //把执行过的回调函数包,从一个列队倒入另一个列队
                 var self = this ;
                 if ( typeof obj === "number" ){ //如果是延时操作
                     var timeoutID = setTimeout( function (){
                         self._fire(okng,self.before(args,result))
                     },obj)
                     this .onabort = function (){
                         clearTimeout(timeoutID );
                     }
                 } else if (obj.arr.length){ //如果是并行操作
                     var i = 0, d;
                     while (d = obj.arr[i++]){
                         d.fire(args)
                     }
                 } else { //如果是串行操作
                     try { //
                         result = obj[okng].apply( this ,args);
                     } catch (e){
                         type = "ng" ;
                         result = e;
                     }
                     this ._fire(type, this .before(args,result))
                 }
             } else { //队列执行完毕,还原
                 ( this .after || Deferred.ok)(result);
                 this ._firing = this ._fired;
                 this ._fired = [];
             }
             return this ;
         },
         fire: function (){ //执行正向列队
             return this ._fire( "ok" , this .before(arguments));
         },
         error: function (){ //执行负向列队
             return this ._fire( "ng" , this .before(arguments));
         },
 
         abort: function (){ //中止列队
             ( this .onabort || Deferred.ok)();
             return this ;
         },
         //每次执行用户回调函数前都执行此函数,返回一个数组
         before: function (args,result){
            return result ? result instanceof Array ? result : [result] : A_slice.call(args)
         },
         //并行操作,并把所有的子线程的结果作为主线程的下一个操作的参数
         paiallel : function (fns) {
             var self = Deferred.get( this ),
             obj = {
                 ok:Deferred.ok,
                 ng:Deferred.ng,
                 arr:[]
             },
             count = 0,
             values = {}
             for ( var key in fns){ //参数可以是一个对象或数组
                 if (fns.hasOwnProperty(key)){
                     ( function (key,fn){
                         if ( typeof fn == "function" )
                             fn = Deferred(fn);
                         fn.then( function (value){
                             values[key] = value;
                             if (--count <= 0){
                                 if (fns instanceof Array){ //如果是数组强制转换为数组
                                     values.length = fns.length;
                                     values = A_slice.call(values)
                                 }
                                 self._fire( "ok" ,[values])
                             }
                         }).once( function (e){
                             self._fire( "ng" ,[e])
                         });
                         obj.arr.push(fn);
                         count++
                     })(key,fns[key])
                 }
             }
             self.onabort = function (){
                 var i = 0, d;
                 while (d = obj.arr[i++]){
                     d.abort();
                 }
             }
             self._firing.push(obj);
             return self
         },
         //处理相近的迭代操作
         loop : function (obj, fn, result) {
             obj = {
                 begin : obj.begin || 0,
                 end   : ( typeof obj.end == "number" ) ? obj.end : obj - 1,
                 step  : obj.step  || 1,
                 last  : false ,
                 prev  : null
             }
             var step = obj.step,
             _loop = function (i,obj){
                 if (i <= obj.end) {
                     if ((i + step) > obj.end) {
                         obj.last = true ;
                         obj.step = obj.end - i + 1;
                     }
                     obj.prev = result;
                     result = fn.call(obj,i);
                     Deferred.get(result).then(_loop).fire(i+step,obj);
                 } else {
                     return result;
                 }
             }
             return (obj.begin <= obj.end) ? Deferred.get( this ).then(_loop).fire(obj.begin,obj) : null ;
         }
     }
     //将原型方法转换为类方法
     "loop wait then once paiallel" .replace(/\w+/g, function (method){
         Deferred[method] = Deferred.prototype[method]
     });
})();

Deferred提供的接口其实不算多,then once loop wait paialle就这五个,我们可以new一个实例出来,用它的实例方法,可以直接用类名加方法名,其实里面还是new了一个实例。另外,还有两个专门用于 复写的方法,before与after。before执行于每个回调函数之前,after执行于所有回调之后,相当于complete了。既然是列队,就 有入队操作与出队操作,我不可能使用queue与dequeue这样土的命名。queue换成两个时间状语,then与once,相当于jQuery的 done、fail,或dojo的addCallback、addErrback。dequeue则用fire与error取替,jQuery1.5的 beta版也曾经用过fire,不知为何换成resolve这样难记的单词。好了,我们先不管其他API,现在就试试身手吧。

       var log = function (s) {
         window.console && window.console.log(s);
       }
       dom.Deferred( function () {
         log(1); //1
       })
       .then( function () {
         log(2); //2
       })
       .then( function () {
         log(3); //3
       })
       .fire();
//如果不使用异步列队,实现这种效果,就需要用套嵌函数
/*
       var fun = function(fn){
         fn()
       };
       fun(function(){
         log(1);
         fun(function(){
           log(2);
           fun(function(){
             log(3);
           })
         });
       });
*/

当然,现在是同步操作。注意,第一个回调函数是作为构造器的参数传入,可以节约了一个then^_^。

默认如果回调函数存在返回值,它会把它作为下一个回调函数的参数传入,如:

dom.Deferred( function (a) {
   a +=  10
   log(a); //11
   return a
})
.then( function (b) {
   b += 12
   log(b); //23
   return b
})
.then( function (c) {
   c += 130
   log(c); //153
})
.fire(1);

我们可以重载before函数,让它的结果不影响下一个回调函数。在多投事件中,我们也可以在before中定义,如果返回false,就中断队列了。

我们再来看它如何处理异常。dom.Deferred的负向列队与jQuery的是完全不同的,jQuery的只是正向列队的一个克隆,而在dom.Deferred中,负向列队只是用于突发情况,是配角。

dom.Deferred( function () {
   log(1111111111) //11111111111
}).
   then( function () {
   throw "error!" ; //发生错误
}).
   then( function (e) { //这个回调函数不执行
   log(e+ "222222" )
   return 2222222
}).
   once( function (e){ //直到 遇上我们自定义的负向回调
   log(e+ '333333' ) //error!333333

你可能感兴趣的:(编程,JavaScript,jQuery,prototype,Mootools)