一步一步实现基于Task的Promise库(四)无参数的WorkItem
接着上一篇我直接给出代码,现在支持了new Task(), then(), all(), any() 这些不传参的调用方式。
1 (function(){ 2 var isFunction = function (target) { 3 return target instanceof Function; 4 }; 5 var isArray = function (target) { 6 return target instanceof Array; 7 }; 8 9 //自定义事件管理(代码摘抄自http://www.cnblogs.com/dolphinX/p/3254017.html) 10 var EventManager = function(){ 11 this.handlers = {}; 12 }; 13 EventManager.prototype = { 14 constructor: EventManager, 15 addHandler: function(type, handler){ 16 if(typeof this.handlers[type] == 'undefined'){ 17 this.handlers[type] = new Array(); 18 } 19 this.handlers[type].push(handler); 20 }, 21 removeHandler: function(type, handler){ 22 if(this.handlers[type] instanceof Array){ 23 var handlers = this.handlers[type]; 24 for(var i=0; i<handlers.length; i++){ 25 if(handler[i] == handler){ 26 handlers.splice(i, 1); 27 break; 28 } 29 } 30 } 31 }, 32 trigger: function(type, event){ 33 /* 34 if(!event.target){ 35 event.target = this; 36 } 37 */ 38 if(this.handlers[type] instanceof Array){ 39 var handlers = this.handlers[type]; 40 for(var i=0; i<handlers.length; i++){ 41 handlers[i](event); 42 } 43 } 44 } 45 }; 46 47 var WorkItem = function(arrayArgs){ 48 var _subItems = []; 49 var _checkFunc = function(args){ 50 if(isFunction(args[0])){ 51 if(args.length == 2 && isArray(args[1])){ 52 _subItems.push({'isFunc': true, 'func': args[0], 'args': args[1]}); 53 } 54 else{ 55 _subItems.push({'isFunc': true, 'func': args[0], 'args': args.slice(1)}); 56 } 57 return true; 58 } 59 return false; 60 }; 61 var _checkTask = function(task){ 62 if(task instanceof Task){ 63 _subItems.push({'isFunc': false, 'task': task}); 64 } 65 }; 66 if(!_checkFunc(arrayArgs)){ 67 for(var i=0; i<arrayArgs.length; i++){ 68 if(!_checkFunc(arrayArgs[i])){ 69 _checkTask(arrayArgs[i]); 70 } 71 } 72 } 73 var _startSubItem = function(subItemIndex, context){ 74 var subItem = _subItems[subItemIndex]; 75 if(subItem.isFunc){ 76 var workItemCxt = context.getWorkItemContext(subItemIndex); 77 subItem.func.apply(workItemCxt, subItem.args); 78 } 79 else{ 80 if(subItem.task.getStatus() == TaskStatus.finished){ 81 context.end(subItem.task.getOutput(), subItemIndex) 82 } 83 else{ 84 subItem.task.finished(function(output){ 85 context.end(output, subItemIndex); 86 }); 87 subItem.task.start(context.inputParams); 88 } 89 } 90 }; 91 92 this.condition = ""; 93 this.start = function(context){ 94 context.setItemsCount(_subItems.length); 95 for(var i=0; i<_subItems.length; i++){ 96 _startSubItem(i, context); 97 } 98 } 99 }; 100 //无参数的WorkItem,用于对前一个WorkItem的条件判断,例如all(); 101 //ConditionWorkItem和WorkItem可以看做实现了一个接口{condition:string,start:function} 102 var ConditionWorkItem = function(){ 103 this.condition = ""; 104 this.start = function(context){ 105 context.triggerEnd(); 106 } 107 }; 108 109 var Context = function(endCallback, inputParams){ 110 var _this = this; 111 var _rawOutputParams = []; 112 var _itemCount; 113 //如果无需Test,_isNonTest就等于true(当调用triggerEnd方法时,就应该无需Test,直接下一个WorkItem) 114 var _isNonTest = false; 115 var _condition = { 116 then: function(){ 117 _this.outputParams = _rawOutputParams[0].value; 118 return true; 119 }, 120 all: function(){ 121 _this.outputParams = []; 122 for(var i=0; i<_itemCount; i++){ 123 if(_rawOutputParams[i]){ 124 _this.outputParams[i] = _rawOutputParams[i].value; 125 } 126 else{ 127 return false; 128 } 129 } 130 return true; 131 }, 132 any: function(){ 133 for(var i=0; i<_itemCount; i++){ 134 if(_rawOutputParams[i]){ 135 _this.outputParams = _rawOutputParams[i].value; 136 return true; 137 } 138 } 139 return false; 140 } 141 }; 142 143 this.inputParams = inputParams; 144 this.outputParams = null; 145 this.setItemsCount = function(itemCount){ 146 _itemCount = itemCount; 147 }; 148 this.testCondition = function(key){ 149 //如果无需Test直接返回true,否则才用Test 150 return _isNonTest || _condition[key](); 151 }; 152 this.end = function(output, index){ 153 _rawOutputParams[index] = { 154 value: output 155 }; 156 if(endCallback){ 157 endCallback(output); 158 } 159 }; 160 this.getWorkItemContext = function(index){ 161 return { 162 param: _this.inputParams, 163 end: function(output){ 164 _this.end(output, index); 165 } 166 }; 167 }; 168 //手动触发EndCallback,(这个上下文设置成无需Test,this.outputParams就设置成this.inputParams,这样参数就可以传递到下一个WorkItem了) 169 this.triggerEnd = function(){ 170 _isNonTest = true; 171 _this.outputParams = _this.inputParams; 172 if(endCallback){ 173 endCallback(_this.outputParams); 174 } 175 }; 176 }; 177 178 var TaskStatus = { 179 //未开始 180 pending: 0, 181 //正在进行 182 doing: 1, 183 //已完成 184 finished: 2 185 }; 186 187 window.Task = function(){ 188 var _status = TaskStatus.pending; 189 var _wItemQueue = [], _currentItem, _currentContext; 190 var _eventManager = new EventManager(); 191 var _output; 192 var _initWorkItem = function(args){ 193 var item; 194 if(args.length == 0){ 195 item = new ConditionWorkItem(); 196 } 197 else{ 198 var arrayArgs = []; 199 for(var i=0; i<args.length; i++){ 200 arrayArgs[i] = args[i]; 201 } 202 item = new WorkItem(arrayArgs); 203 } 204 _wItemQueue.push(item); 205 return item; 206 }; 207 var _setItemCondition = function(item, condition){ 208 if(condition){ 209 item.condition = condition; 210 } 211 }; 212 var _tryDoNextItem = function(output){ 213 var next = _getCurNextItem(); 214 if(next){ 215 if(_currentContext.testCondition(next.condition)){ 216 _currentItem = next; 217 _doCurrentItem(); 218 } 219 } 220 else{ 221 _status = TaskStatus.finished; 222 _output = output; 223 _eventManager.trigger("finish", output); 224 } 225 }; 226 var _doCurrentItem = function(contextParam){ 227 if(contextParam) { 228 _currentContext = new Context(_tryDoNextItem, contextParam); 229 } 230 else{ 231 if(_currentContext){ 232 _currentContext = new Context(_tryDoNextItem, _currentContext.outputParams); 233 } 234 else{ 235 _currentContext = new Context(_tryDoNextItem); 236 } 237 } 238 _currentItem.start(_currentContext); 239 }; 240 var _getCurNextItem = function(){ 241 var i=0; 242 for(; i<_wItemQueue.length; i++){ 243 if(_currentItem == _wItemQueue[i]){ 244 break; 245 } 246 } 247 return _wItemQueue[i + 1]; 248 }; 249 _currentItem = _initWorkItem(arguments); 250 251 this.getStatus = function(){ 252 return _status; 253 }; 254 this.getOutput = function(){ 255 return _output; 256 }; 257 this.finished = function(callback){ 258 if(callback){ 259 _eventManager.addHandler("finish", callback); 260 } 261 }; 262 this.start = function(contextParam){ 263 if(_status == TaskStatus.pending){ 264 _status = TaskStatus.doing; 265 _doCurrentItem(contextParam); 266 } 267 return this; 268 }; 269 this.then = function(){ 270 var workItem = _initWorkItem(arguments); 271 _setItemCondition(workItem, 'then'); 272 return this; 273 }; 274 this.all = function(){ 275 //这个arguments现在可能是空的了! 276 var workItem = _initWorkItem(arguments); 277 _setItemCondition(workItem, 'all'); 278 return this; 279 }; 280 this.any = function(){ 281 var workItem = _initWorkItem(arguments); 282 _setItemCondition(workItem, 'any'); 283 return this; 284 }; 285 }; 286 })();
var task = new Task([readFile, "aa.txt"], [readFile, "bb.txt"]).all(writeBack, "cc.txt").start();
现在上面的调用形式同样可以用下面的代码代替:
1 //测试1 2 var taskExp_1 = new Task([readFile, "aa.txt"], [readFile, "bb.txt"]).all().then(writeBack, "cc.txt").start(); 3 //测试2 4 var taskExp_2 = new Task([readFile, "aa.txt"], [readFile, "bb.txt"]).all(); 5 var taskExp_3 = new Task(taskExp_2).then(writeBack, "cc.txt").start(); 6 //测试3 7 var taskExp_4 = new Task([readFile, "aa.txt"], [readFile, "bb.txt"]).all(); 8 var taskExp_5 = new Task().then(taskExp_4).then(writeBack, "cc.txt").start();
在下一篇,我们再来实现waitFor方法。