首页 > 代码库 > 小练习—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类特性,可能有些人会觉得不爽,不完美。
这个问题嘛,我也纠结过,但没办法,小生的水平也就这样(能写成现在这模样,还是借用了公司老鸟的设计思路的前提之下呀...)
而且为了确保这个类的轻,适当的留一点缺陷,在使用的时候注意点,也未尝不可。
最后,如果有神马好的见解,或者发现代码中有什么问题或不足,欢迎留言,好让我改进。
小生在此先谢过了~~