标签:style blog http java color 使用 os 文件
在上一篇中我们已经初步完成了Task类,如果仅仅是这些,那么没有多大意义,因为网上这类js库有很多,现在我们来些更复杂的使用场景。
如果我们现在有这样一个需求:我们要先读取aa.txt的内容,然后去后台解析,同时bb.txt也要读取解析,然后当两个文件都解析完了,我们还要合并两部分内容存到cc.txt中,最后发个通知说ok了。。需求很变态,但是我还是想问有没有好的办法呢?按照最原始的嵌套回调的写法好像不是那么容易了,因为你没法知道aa.txt和bb.txt两个文件的读取解析谁先完成,所以你除了要关注逻辑本身还得花费一些功夫在这个谁先谁后上面,如果需求变了。。。然后就没有然后了,想想我就要吐血。
那么回到上一章的Task类,Task有没有办法解决这个问题呢?回答是有,先看下面的代码:
1 var taskExp_1 = new Task(readFile, ["aa.txt"]).then(resolveFile, ["/service/fileResolve.ashx?file=aa.txt"]); 2 var taskExp_2 = new Task(readFile, ["bb.txt"]).then(resolveFile, ["/service/fileResolve.ashx?file=bb.txt"]); 3 var taskExp_3 = new Task(taskExp_1, taskExp_2).all(writeFile, ["cc.txt"]).then(sendMail).start();
如同需求描述的一样,taskExp_1,taskExp_2分别描述了aa.txt和bb.txt的读取和解析,taskExp_3的构造方法里面包括了taskExp_1,taskExp_2,后面的all方法表示需要taskExp_1和taskExp_2都完成才能执行writeFile,最后才是sendMail。这样一来彻底解决了这类需求带来的复杂性,如果把all改成any表示taskExp_1和taskExp_2完成其中一个就能执行writeFile。
在实现all和any方法之前,先看一看我们将要面临哪些问题:
继续上一章所写的Task类来扩展这两个方法,现在我们以一个标准的js库形式来书写实现细节,这个库命名为Task.js:
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 //WorkItem的参数和Task一样,有下面几种形式,不过最终存到subItems里面的元素只有两种类型.一种是方法和它的参数,一种是Task对象 48 //1.(func, [funcArg1, funcArg2]) 49 //2.(func, funcArg1, funcArg2, ....) 50 //3.(task1, task2, ....) 51 //4.([func, funcArg1, funcArg2, ....] 52 // ,[func, [funcArg1, funcArg2]] 53 // ,task1 54 // , .... 55 // ) 56 var WorkItem = function(arrayArgs){ 57 //WorkItem其实是一组异步操作的集合,_subItems就是这个集合 58 var _subItems = []; 59 var _checkFunc = function(args){ 60 if(isFunction(args[0])){ 61 if(args.length == 2 && isArray(args[1])){ 62 _subItems.push({‘isFunc‘: true, ‘func‘: args[0], ‘args‘: args[1]}); 63 } 64 else{ 65 _subItems.push({‘isFunc‘: true, ‘func‘: args[0], ‘args‘: args.slice(1)}); 66 } 67 return true; 68 } 69 return false; 70 }; 71 var _checkTask = function(task){ 72 if(task instanceof Task){ 73 _subItems.push({‘isFunc‘: false, ‘task‘: task}); 74 } 75 }; 76 //这里是对形参的检测,看看是否满足上面的4种形式 77 if(!_checkFunc(arrayArgs)){ 78 for(var i=0; i<arrayArgs.length; i++){ 79 if(!_checkFunc(arrayArgs[i])){ 80 _checkTask(arrayArgs[i]); 81 } 82 } 83 } 84 85 //开始执行SubItem 86 var _startSubItem = function(subItemIndex, context){ 87 var subItem = _subItems[subItemIndex]; 88 //如果subItem是方法和它的参数 89 if(subItem.isFunc){ 90 //先获取方法的上下文环境(就是方法里面的this) 91 var workItemCxt = context.getWorkItemContext(subItemIndex); 92 //再执行方法 93 subItem.func.apply(workItemCxt, subItem.args); 94 } 95 //如果subItem是Task对象 96 else{ 97 //如果task已经完成 98 if(subItem.task.getStatus() == TaskStatus.finished){ 99 context.end(subItem.task.getOutput(), subItemIndex) 100 } 101 else{ 102 //先注册Task对象的完成事件 103 subItem.task.finished(function(output){ 104 context.end(output, subItemIndex); 105 }); 106 //再启动这个task 107 subItem.task.start(context.inputParams); 108 } 109 } 110 }; 111 this.condition = ""; 112 //开始执行WorkItem 113 this.start = function(context){ 114 context.setItemsCount(_subItems.length); 115 for(var i=0; i<_subItems.length; i++){ 116 _startSubItem(i, context); 117 } 118 } 119 }; 120 121 //异步操作上下文 122 var Context = function(endCallback, inputParams){ 123 var _this = this; 124 //Workitem里面的每一个Item会按各自的索引把返回值存到_rawOutputParams里,这样做的目的是为了方便后续操作的先决条件判断 125 var _rawOutputParams = []; 126 //子Item个数 127 var _itemCount; 128 //先决条件的判断方法集合,判断的同时也会更新outputParams 129 var _condition = { 130 then: function(){ 131 _this.outputParams = _rawOutputParams[0].value; 132 return true; 133 }, 134 all: function(){ 135 _this.outputParams = []; 136 for(var i=0; i<_itemCount; i++){ 137 if(_rawOutputParams[i]){ 138 _this.outputParams[i] = _rawOutputParams[i].value; 139 } 140 else{ 141 return false; 142 } 143 } 144 return true; 145 }, 146 any: function(){ 147 for(var i=0; i<_itemCount; i++){ 148 if(_rawOutputParams[i]){ 149 _this.outputParams = _rawOutputParams[i].value; 150 return true; 151 } 152 } 153 return false; 154 } 155 }; 156 157 //异步操作的输入操作 158 this.inputParams = inputParams; 159 //最终异步操作上下文结束时的输出参数(一般是一个操作的输出参数,all条件的输出参数比较特殊,是一个数组,每一个元素对应每一个子Workitem的输出参数) 160 this.outputParams = null; 161 this.setItemsCount = function(itemCount){ 162 _itemCount = itemCount; 163 }; 164 //测试_rawOutputParams对于key条件是否通过 165 this.testCondition = function(key){ 166 return _condition[key](); 167 }; 168 //索引为index的子Workitem完成时会调用这个方法 169 this.end = function(output, index){ 170 _rawOutputParams[index] = { 171 value: output 172 }; 173 if(endCallback){ 174 endCallback(output); 175 } 176 }; 177 //生成一个子Workitem执行时的上下文环境 178 this.getWorkItemContext = function(index){ 179 //这个是子Item的上下文对象(就是this) 180 return { 181 //传递给上下文的参数 182 param: _this.inputParams, 183 //调用end方法告知异步操作的完成 184 end: function(output){ 185 _this.end(output, index); 186 } 187 }; 188 }; 189 }; 190 191 //Task的状态 192 var TaskStatus = { 193 //未开始 194 pending: 0, 195 //正在进行 196 doing: 1, 197 //已完成 198 finished: 2 199 }; 200 201 //不定义具体形参,直接使用arguments 202 window.Task = function(){ 203 var _status = TaskStatus.pending; 204 var _wItemQueue = [], _currentItem, _currentContext; 205 var _eventManager = new EventManager(); 206 var _output; 207 //初始化一个WorkItem,并添加到执行队列中 208 var _initWorkItem = function(args){ 209 var arrayArgs = []; 210 for(var i=0; i<args.length; i++){ 211 arrayArgs[i] = args[i]; 212 } 213 var item = new WorkItem(arrayArgs); 214 _wItemQueue.push(item); 215 return item; 216 }; 217 //设置item的先决条件 218 var _setItemCondition = function(item, condition){ 219 if(condition){ 220 item.condition = condition; 221 } 222 }; 223 //试着执行下一个异步操作,如果这个操作满足他的先决条件,那就执行;如果已经式最后一个异步操作了,就触发完成事件 224 var _tryDoNextItem = function(output){ 225 var next = _getCurNextItem(); 226 if(next){ 227 if(_currentContext.testCondition(next.condition)){ 228 _currentItem = next; 229 _doCurrentItem(); 230 } 231 } 232 else{ 233 _status = TaskStatus.finished; 234 _output = output; 235 _eventManager.trigger("finish", output); 236 } 237 }; 238 //执行当前的Workitem 239 var _doCurrentItem = function(contextParam){ 240 if(contextParam) { 241 _currentContext = new Context(_tryDoNextItem, contextParam); 242 } 243 else{ 244 if(_currentContext){ 245 _currentContext = new Context(_tryDoNextItem, _currentContext.outputParams); 246 } 247 else{ 248 _currentContext = new Context(_tryDoNextItem); 249 } 250 } 251 _currentItem.start(_currentContext); 252 }; 253 //获取下一个异步操作,如果已经是最后一个了返回undefined 254 var _getCurNextItem = function(){ 255 var i=0; 256 for(; i<_wItemQueue.length; i++){ 257 if(_currentItem == _wItemQueue[i]){ 258 break; 259 } 260 } 261 return _wItemQueue[i + 1]; 262 }; 263 _currentItem = _initWorkItem(arguments); 264 265 //获取Task当前状态 266 this.getStatus = function(){ 267 return _status; 268 }; 269 //获取Task完成时的输出参数 270 this.getOutput = function(){ 271 return _output; 272 }; 273 //注册完成事件 274 this.finished = function(callback){ 275 if(callback){ 276 _eventManager.addHandler("finish", callback); 277 } 278 }; 279 //任务开始(把do改成start是因为在ie下有问题,ie会把do当做保留字) 280 //contextParam是一个上下文参数,可以在异步方法中通过this.Param引用 281 this.start = function(contextParam){ 282 if(_status == TaskStatus.pending){ 283 _status = TaskStatus.doing; 284 _doCurrentItem(contextParam); 285 } 286 return this; 287 }; 288 this.then = function(){ 289 var workItem = _initWorkItem(arguments); 290 _setItemCondition(workItem, ‘then‘); 291 return this; 292 }; 293 this.all = function(){ 294 var workItem = _initWorkItem(arguments); 295 _setItemCondition(workItem, ‘all‘); 296 return this; 297 }; 298 this.any = function(){ 299 var workItem = _initWorkItem(arguments); 300 _setItemCondition(workItem, ‘any‘); 301 return this; 302 }; 303 }; 304 })();
除了之前的Task类之外,还添加了WorkItem类用来表示一组异步操作。代码有点多,细节请看注释。
最后给一个demo:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title></title> 5 </head> 6 <body> 7 <script src="jquery-latest.js" type="text/javascript"></script> 8 <script type="text/javascript"> 9 //promise 10 //读取文件的原始内容 11 var readFile = function(fileName){ 12 var _this = this; 13 window.setTimeout(function(){ 14 var rawContent = "xxxxxxxx (" + fileName + ")"; 15 console.log("read ‘" + fileName + "‘ complete. rawContent is " + rawContent); 16 _this.end(rawContent); 17 }, 1000); 18 }; 19 //请求服务器来解析原始内容,得到真正的内容 20 var resolveFile = function(serverUrl){ 21 var _this = this; 22 var rawContent = _this.param; 23 window.setTimeout(function(){ 24 var realContent = "Greeting (" + serverUrl + ")"; 25 console.log("resolve file complete. realContent is " + realContent); 26 _this.end(realContent); 27 }, 1500); 28 }; 29 //把真正的内容写入一开始的文件 30 var writeFile = function (fileName) { 31 var _this = this; 32 window.setTimeout(function(){ 33 console.log("writeBack1 param[0] is " + _this.param[0] + " ;param[1] is " + _this.param[1]); 34 _this.end(); 35 }, 2000); 36 }; 37 var sendMail = function(){ 38 var _this = this; 39 window.setTimeout(function(){ 40 console.log("sendMail finished"); 41 _this.end(); 42 }, 1000); 43 }; 44 45 //问题: 46 //1.WorkItem的参数多样化 (一些没有意义的使用形式?) 47 //2.new Task(),then,all,any方法参数可以接收Task对象 (Task完成的事件通知?) 48 //3.一组异步操作的返回值如何传递到下一组异步操作 (对于all方法如何接收前一组异步操作的所有输出参数) 49 //4.当不同状态的Task作为参数传递到另一个Task中时(未开始执行的,正在执行的,执行完成的),应该怎么处理 (Task状态管理) 50 (function(){ 51 var isFunction = function(target){ 52 return target instanceof Function; 53 }; 54 var isArray = function(target){ 55 return target instanceof Array; 56 }; 57 58 //自定义事件管理(代码摘抄自http://www.cnblogs.com/dolphinX/p/3254017.html) 59 var EventManager = function(){ 60 this.handlers = {}; 61 }; 62 EventManager.prototype = { 63 constructor: EventManager, 64 addHandler: function(type, handler){ 65 if(typeof this.handlers[type] == ‘undefined‘){ 66 this.handlers[type] = new Array(); 67 } 68 this.handlers[type].push(handler); 69 }, 70 removeHandler: function(type, handler){ 71 if(this.handlers[type] instanceof Array){ 72 var handlers = this.handlers[type]; 73 for(var i=0; i<handlers.length; i++){ 74 if(handler[i] == handler){ 75 handlers.splice(i, 1); 76 break; 77 } 78 } 79 } 80 }, 81 trigger: function(type, event){ 82 /* 83 if(!event.target){ 84 event.target = this; 85 } 86 */ 87 if(this.handlers[type] instanceof Array){ 88 var handlers = this.handlers[type]; 89 for(var i=0; i<handlers.length; i++){ 90 handlers[i](event); 91 } 92 } 93 } 94 }; 95 96 //WorkItem的参数和Task一样,有下面几种形式,不过最终存到subItems里面的元素只有两种类型.一种是方法和它的参数,一种是Task对象 97 //1.(func, [funcArg1, funcArg2]) 98 //2.(func, funcArg1, funcArg2, ....) 99 //3.(task1, task2, ....) 100 //4.([func, funcArg1, funcArg2, ....] 101 // ,[func, [funcArg1, funcArg2]] 102 // ,task1 103 // , .... 104 // ) 105 var WorkItem = function(arrayArgs){ 106 //WorkItem其实是一组异步操作的集合,_subItems就是这个集合 107 var _subItems = []; 108 var _checkFunc = function(args){ 109 if(isFunction(args[0])){ 110 if(args.length == 2 && isArray(args[1])){ 111 _subItems.push({‘isFunc‘: true, ‘func‘: args[0], ‘args‘: args[1]}); 112 } 113 else{ 114 _subItems.push({‘isFunc‘: true, ‘func‘: args[0], ‘args‘: args.slice(1)}); 115 } 116 return true; 117 } 118 return false; 119 }; 120 var _checkTask = function(task){ 121 if(task instanceof Task){ 122 _subItems.push({‘isFunc‘: false, ‘task‘: task}); 123 } 124 }; 125 //这里是对形参的检测,看看是否满足上面的4种形式 126 if(!_checkFunc(arrayArgs)){ 127 for(var i=0; i<arrayArgs.length; i++){ 128 if(!_checkFunc(arrayArgs[i])){ 129 _checkTask(arrayArgs[i]); 130 } 131 } 132 } 133 134 //开始执行SubItem 135 var _startSubItem = function(subItemIndex, context){ 136 var subItem = _subItems[subItemIndex]; 137 //如果subItem是方法和它的参数 138 if(subItem.isFunc){ 139 //先获取方法的上下文环境(就是方法里面的this) 140 var workItemCxt = context.getWorkItemContext(subItemIndex); 141 //再执行方法 142 subItem.func.apply(workItemCxt, subItem.args); 143 } 144 //如果subItem是Task对象 145 else{ 146 //如果task已经完成 147 if(subItem.task.getStatus() == TaskStatus.finished){ 148 context.end(subItem.task.getOutput(), subItemIndex) 149 } 150 else{ 151 //先注册Task对象的完成事件 152 subItem.task.finished(function(output){ 153 context.end(output, subItemIndex); 154 }); 155 //再启动这个task 156 subItem.task.start(context.inputParams); 157 } 158 } 159 }; 160 this.condition = ""; 161 //开始执行WorkItem 162 this.start = function(context){ 163 context.setItemsCount(_subItems.length); 164 for(var i=0; i<_subItems.length; i++){ 165 _startSubItem(i, context); 166 } 167 } 168 }; 169 170 //异步操作上下文 171 var Context = function(endCallback, inputParams){ 172 var _this = this; 173 //Workitem里面的每一个Item会按各自的索引把返回值存到_rawOutputParams里,这样做的目的是为了方便后续操作的先决条件判断 174 var _rawOutputParams = []; 175 //子Item个数 176 var _itemCount; 177 //先决条件的判断方法集合,判断的同时也会更新outputParams 178 var _condition = { 179 then: function(){ 180 _this.outputParams = _rawOutputParams[0].value; 181 return true; 182 }, 183 all: function(){ 184 _this.outputParams = []; 185 for(var i=0; i<_itemCount; i++){ 186 if(_rawOutputParams[i]){ 187 _this.outputParams[i] = _rawOutputParams[i].value; 188 } 189 else{ 190 return false; 191 } 192 } 193 return true; 194 }, 195 any: function(){ 196 for(var i=0; i<_itemCount; i++){ 197 if(_rawOutputParams[i]){ 198 _this.outputParams = _rawOutputParams[i].value; 199 return true; 200 } 201 } 202 return false; 203 } 204 }; 205 206 //异步操作的输入操作 207 this.inputParams = inputParams; 208 //最终异步操作上下文结束时的输出参数(一般是一个操作的输出参数,all条件的输出参数比较特殊,是一个数组,每一个元素对应每一个子Workitem的输出参数) 209 this.outputParams = null; 210 this.setItemsCount = function(itemCount){ 211 _itemCount = itemCount; 212 }; 213 //测试_rawOutputParams对于key条件是否通过 214 this.testCondition = function(key){ 215 return _condition[key](); 216 }; 217 //索引为index的子Workitem完成时会调用这个方法 218 this.end = function(output, index){ 219 _rawOutputParams[index] = { 220 value: output 221 }; 222 if(endCallback){ 223 endCallback(output); 224 } 225 }; 226 //生成一个子Workitem执行时的上下文环境 227 this.getWorkItemContext = function(index){ 228 //这个是子Item的上下文对象(就是this) 229 return { 230 //传递给上下文的参数 231 param: _this.inputParams, 232 //调用end方法告知异步操作的完成 233 end: function(output){ 234 _this.end(output, index); 235 } 236 }; 237 }; 238 }; 239 240 //Task的状态 241 var TaskStatus = { 242 //未开始 243 pending: 0, 244 //正在进行 245 doing: 1, 246 //已完成 247 finished: 2 248 }; 249 250 //不定义具体形参,直接使用arguments 251 window.Task = function(){ 252 var _status = TaskStatus.pending; 253 var _wItemQueue = [], _currentItem, _currentContext; 254 var _eventManager = new EventManager(); 255 var _output; 256 //初始化一个WorkItem,并添加到执行队列中 257 var _initWorkItem = function(args){ 258 var arrayArgs = []; 259 for(var i=0; i<args.length; i++){ 260 arrayArgs[i] = args[i]; 261 } 262 var item = new WorkItem(arrayArgs); 263 _wItemQueue.push(item); 264 return item; 265 }; 266 //设置item的先决条件 267 var _setItemCondition = function(item, condition){ 268 if(condition){ 269 item.condition = condition; 270 } 271 }; 272 //试着执行下一个异步操作,如果这个操作满足他的先决条件,那就执行;如果已经式最后一个异步操作了,就触发完成事件 273 var _tryDoNextItem = function(output){ 274 var next = _getCurNextItem(); 275 if(next){ 276 if(_currentContext.testCondition(next.condition)){ 277 _currentItem = next; 278 _doCurrentItem(); 279 } 280 } 281 else{ 282 _status = TaskStatus.finished; 283 _output = output; 284 _eventManager.trigger("finish", output); 285 } 286 }; 287 //执行当前的Workitem 288 var _doCurrentItem = function(contextParam){ 289 if(contextParam) { 290 _currentContext = new Context(_tryDoNextItem, contextParam); 291 } 292 else{ 293 if(_currentContext){ 294 _currentContext = new Context(_tryDoNextItem, _currentContext.outputParams); 295 } 296 else{ 297 _currentContext = new Context(_tryDoNextItem); 298 } 299 } 300 _currentItem.start(_currentContext); 301 }; 302 //获取下一个异步操作,如果已经是最后一个了返回undefined 303 var _getCurNextItem = function(){ 304 var i=0; 305 for(; i<_wItemQueue.length; i++){ 306 if(_currentItem == _wItemQueue[i]){ 307 break; 308 } 309 } 310 return _wItemQueue[i + 1]; 311 }; 312 _currentItem = _initWorkItem(arguments); 313 314 //获取Task当前状态 315 this.getStatus = function(){ 316 return _status; 317 }; 318 //获取Task完成时的输出参数 319 this.getOutput = function(){ 320 return _output; 321 }; 322 //注册完成事件 323 this.finished = function(callback){ 324 if(callback){ 325 _eventManager.addHandler("finish", callback); 326 } 327 }; 328 //任务开始(把do改成start是因为在ie下有问题,ie会把do当做保留字) 329 //contextParam是一个上下文参数,可以在异步方法中通过this.Param引用 330 this.start = function(contextParam){ 331 if(_status == TaskStatus.pending){ 332 _status = TaskStatus.doing; 333 _doCurrentItem(contextParam); 334 } 335 return this; 336 }; 337 this.then = function(){ 338 var workItem = _initWorkItem(arguments); 339 _setItemCondition(workItem, ‘then‘); 340 return this; 341 }; 342 this.all = function(){ 343 var workItem = _initWorkItem(arguments); 344 _setItemCondition(workItem, ‘all‘); 345 return this; 346 }; 347 this.any = function(){ 348 var workItem = _initWorkItem(arguments); 349 _setItemCondition(workItem, ‘any‘); 350 return this; 351 }; 352 }; 353 })(); 354 355 356 var taskExp_1 = new Task(readFile, ["aa.txt"]).then(resolveFile, ["/service/fileResolve.ashx?file=aa.txt"]); 357 var taskExp_2 = new Task(readFile, ["bb.txt"]).then(resolveFile, ["/service/fileResolve.ashx?file=bb.txt"]); 358 var taskExp_3 = new Task(taskExp_1, taskExp_2).all(writeFile, ["cc.txt"]).then(sendMail).start(); 359 360 </script> 361 </body> 362 </html>
一步一步实现基于Task的Promise库(二)all和any方法的设计和实现,布布扣,bubuko.com
一步一步实现基于Task的Promise库(二)all和any方法的设计和实现
标签:style blog http java color 使用 os 文件
原文地址:http://www.cnblogs.com/lihao123/p/3869159.html