首页 > 代码库 > 小练习—FuncDelayer—JS函数延迟器

小练习—FuncDelayer—JS函数延迟器

自从看了关于Jquery的deferred对象的一些文章后,森森的感觉到灰常赏心悦目。

但是假如我们平时的工作中,遇到一个项目,不允许我们使用一些长得比较【胖】库,而我们又想用类似deferred对象的功能呢...

又或者说,你觉得deferred对象的使用方式不是很顺手,和你的编程思维习惯有点冲突,导致经常有一种不自在的感觉呢...

然后,今日在公司的基类库中,发现了功能类似的简化类。

它的特点就是精简轻便,又能实现函数延迟执行,而且用法跟我们平时正常的思维很类似。

在花了一段时间窥探其源码后,很是喜欢,也感到非常的实用。

于是乎,昨天在家里闲来无事,自己凭着仅余的3成实现思路印象,自己琢磨出一个函数延迟器工具类。

也算是一个小小的练习吧,哈哈~~

首先上代码,内附丰富注释,相信只要会汉语都能看懂了吧~

(function(window){
    /* === Class DelayedFunc begin === */
    /*
    用 DelayedFunc 类创建的对象包含5属性
    func 【function】 需要触发的函数实体
    allowCount 【int】 已经成功触发的条件数
    conditionCount 【int】 需要触发的条件数
    allowFlag 【bool】 该函数的所有触发条件都已经触发了,其值就为true,否则其值就为false
    conditionStatusList 【Obj】 此对象包含的子属性的数目将等于conditionCount的值,这n个属性的属性名和触发条件名相同
    */
    var DelayedFunc = function(func, conditionCount){
        var _this = this;
        _this.func = func;
        _this.conditionCount = conditionCount;
        _this.allowCount = 0;
        _this.allowFlag = false;
        _this.conditionStatusList = {};
    };

    DelayedFunc.prototype.checkStatus = function(){
        var _this = this;

        if(_this.allowFlag){
            return true;
        }

        if(_this.allowCount == _this.conditionCount){
            _this.allowFlag = true;
            return true;
        }

        return false;
    }

    /* === Class FuncDepot begin === */
    var FuncDepot = function(){
        var _this = this;
        //初始化触发条件列表
        _this.conditionList = {};
        //初始化函数库存列表
        _this.stockList = {};
    };

    /*
    * 参数说明:
    * condition 【Array】 触发条件list
    * func 【function】 满足触发条件时,需要触发的函数
    * */
    FuncDepot.prototype.stockPush = function(condition, func){
        var _this = this,
            condition_length = condition.length,
            curStock = null,
            stockName = condition.join(‘_‘);

        if(typeof _this.stockList[stockName] == "undefined"){

            /*
             如果指定的库存函数对象不存在,就在函数库存列表中插入一个新的库存函数对象
             同时定义一个局部变量,缓存该对象(当前库存函数对象),以便后面的代码快速引用它
            */
            curStock = _this.stockList[stockName] = new DelayedFunc(func, condition_length);

        }else{

            //如果指定的库存函数对象已经存在,直接引用它
            curStock = _this.stockList[stockName];
            curStock.func = func;

        }

        if(curStock.allowFlag){

            //当前库存函数对象 的函数触发标记为true时,直接触发函数
            curStock.func.call();

        }else{

            //遍历触发条件list
            for(var i = 0; i < condition_length; i++){

                var curCondition = condition[i];
                //如果触发条件列表(conditionList)中,对应的触发条件的值为true(该条件已经被触发)
                if(_this.conditionList[curCondition]){

                    //为 当前库存函数对象 插入条件属性,并设置其值为true
                    curStock.conditionStatusList[curCondition] = true;
                    curStock.allowCount++;

                }else{

                    //否则,为 当前库存函数对象 插入条件属性,并设置其值为false
                    curStock.conditionStatusList[curCondition] = false;

                }

            }

            if(curStock.checkStatus()){

                //该函数的所有触发条件都已经触发了
                curStock.func.call();

            }

        }
    };

    /*
     * 参数说明:
     * condition 【Array】 触发条件list
     * */
    FuncDepot.prototype.stockPop = function(condition) {
        var _this = this, condition_length = condition.length, stockItem;

        //遍历函数库存列表
        for(stockItem in _this.stockList){

            for(var i = 0; i < condition_length; i++){

                var curCondition = condition[i];
                //进行库存函数名称匹配,如果库存函数名含有与某个触发条件相同的字符,说明该库存函数含有此触发条件
                if(stockItem.indexOf(curCondition) != -1){

                    var curStock = _this.stockList[stockItem];

                    if(curStock.allowFlag){

                        //如果该函数的所有触发条件都已经满足,直接触发该函数
                        curStock.func.call();
                        //并跳出名称匹配这一层循环,继续遍历函数库存列表,因为没有必要继续做条件匹配了
                        break;

                    }else{

                        //该函数并未所有触发条件满足,且当前正在触发的条件正是未被触发的
                        if(!curStock.conditionStatusList[curCondition]){

                            //将该函数的对应触发条件设为true(已触发),已触发条件数+1
                            curStock.conditionStatusList[curCondition] = true;
                            curStock.allowCount++;

                        }

                        //触发条件相关属性变更完后,判断已触发条件数是否与需要触发的条件数相同,如果相同,则所有触发条件都已触发
                        if(curStock.checkStatus()){

                            //所有触发条件都已触发,allowFlag属性设为true,并直接执行该函数
                            curStock.func.call();
                            //并跳出名称匹配这一层循环,继续遍历函数库存列表,因为没有必要继续做条件匹配了
                            break;

                        }

                    }

                }

            }

        }
    };

    /* === Class FuncDelayer begin === */
    var FuncDelayer = function(){
        var _this = this;
        _this.funcDepot = new FuncDepot();
    };

    /*
     * 参数说明:
     * condition 【String】
     * 触发条件list的表达字符串,各个条件由英文逗号","分隔开
     * 字符串中的所有空格符都会被过滤掉,建议仅用英文和数字组织触发条件名
     * func 【function】 满足触发条件时,需要触发的函数
     * */
    FuncDelayer.prototype.push = function(condition, func){
        var _this = this,
            condition = condition.replace(/ /g, ‘‘).split(‘,‘);

        for(var i = 0, j = condition.length; i < j; i++){

            var curCondition = condition[i];
            //如果该触发条件并未加入触发条件列表中
            if(!_this.funcDepot.conditionList[curCondition]){

                //将该触发条件插入到触发条件列表,触发条件的值为false(未触发)
                _this.funcDepot.conditionList[curCondition] = false;

            }

        }

        _this.funcDepot.stockPush(condition, func);
    };

    /*
     * 参数说明:
     * condition 【String】
     * 触发条件list的表达字符串,各个条件由英文逗号","分隔开
     * 字符串中的所有空格符都会被过滤掉,建议仅用英文和数字组织触发条件名
     * */
    FuncDelayer.prototype.pop = function(condition){
        var _this = this,
            condition = condition.replace(/ /g, ‘‘).split(‘,‘);

        for(var i = 0, j = condition.length; i < j; i++){

            var curCondition = condition[i];
            //如果触发条件列表中包含此触发条件,就会直接将其设为true
            //如果触发条件列表中不包含此触发条件,则会将该触发条件插入到触发条件列表,触发条件的值为true(已触发)
            _this.funcDepot.conditionList[curCondition] = true;

        }

        _this.funcDepot.stockPop(condition);
    };

    window.FuncDelayer = FuncDelayer;
})(window);

接下来说说用法:

首先当然是创建一个funcDelayer对象,这个对象提供了两个方法,push 和 pop,是不是想到数组了?

矮油~人家压根就是抄数组的方法命名嘛,嘎嘎嘎嘎~~

不过,名字虽然一样,用法还是有区别D~

push:用来给函数仓库装进一个函数,并指定该函数的触发条件

pop:触发指定的条件

funcDelayer对象有一个很美的地方,就是,你可以先往函数仓库装进函数,再触发该函数指定的条件,从而达到触发该函数的效果。

也可以先触发某个条件,然后再给函数仓库装进一个函数,并指定该函数的触发条件是先前已经触发了的条件,这样函数在装进仓库后就马上被调用了。

有了这一个特性,此对象就能在异步请求中很好的发挥了。

下面上几个实际应用例子:

//先创建一个funDelayer对象...
var funcDelayer = new FuncDelayer();

//先来个最简单的~
$.ajax({
  ...//省略N行代码,自己补吧
  success: function(data){
    funcDelayer.pop(‘getDataSuccess‘);    
  }    
});

funcDelayer.push(‘getDataSuccess‘, function(){
  alert(‘成功了~~‘);
});
//先创建一个funDelayer对象...
var funcDelayer = new FuncDelayer();

//反过来也行,结果都一样~
$.ajax({
  ...//这里省略N行代码,自己补吧
  success: function(data){
    funcDelayer.push(‘getDataSuccess‘, function(){
      alert(‘成功了~~‘);
    });   
  }    
});

funcDelayer.pop(‘getDataSuccess‘); 
//先创建一个funDelayer对象...
var funcDelayer = new FuncDelayer();

//啥?上面两个例子其实和直接执行回调函数没区别?那这样呢?
$.ajax({
  ...//省略N行代码,自己补吧
  success: function(data){
    funcDelayer.pop(‘getDataSuccessA‘);    
  }    
});

$.ajax({
  ...//省略N行代码,自己补吧
  success: function(data){
    funcDelayer.pop(‘getDataSuccessB‘);    
  }    
});

funcDelayer.push(‘getDataSuccessA, getDataSuccessB‘, function(){
  alert(‘成功了~~这次可是两个异步请求都完成了才会触发哦~~‘);
});

//当然,3个甚至更多的异步请求,照样没问题
//先创建一个funDelayer对象...
var funcDelayer = new FuncDelayer();

//接下来是FunDelayer类的特性展示,下面的代码,请一句一句的,分别在控制台中执行

funcDelayer.push(‘successA‘, function(){
  alert(‘A 成功了~~‘);
});

funcDelayer.push(‘successA, successB‘, function(){
  alert(‘A and B 成功了~~‘);
});

//这句执行后,触发 A 函数
funcDelayer.pop(‘successA‘);

//这句执行后,触发 A 函数,因为 A 函数的触发条件在前面已经触发了,但弹出的信息变了,说明原来的函数被覆盖了
funcDelayer.push(‘successA‘, function(){
  alert(‘A成功了,但原函数被覆盖了~~‘);
});

//这句执行后,触发 A and B 函数
funcDelayer.pop(‘successB‘);

//再一次执行这句,触发 A 函数,同时也触发 A and B 函数,如果条件 A 和 B 前面都已经触发,那么后面只要触发其中任何一个条件,都能触发 A and B 函数
funcDelayer.pop(‘successA‘);

实例就到这里,上面最后一个实例,展示的FunDelayer类特性,可能有些人会觉得不爽,不完美。

这个问题嘛,我也纠结过,但没办法,小生的水平也就这样(能写成现在这模样,还是借用了公司老鸟的设计思路的前提之下呀...)

而且为了确保这个类的轻,适当的留一点缺陷,在使用的时候注意点,也未尝不可。

最后,如果有神马好的见解,或者发现代码中有什么问题或不足,欢迎留言,好让我改进。

小生在此先谢过了~~