上一篇中我们已经初步完成了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方法之前,先看一看我们将要面临哪些问题:

  1. 现在new Task(),then(),all(),any()方法的形参可以是多个异步操作了,并且每个异步操作可以是一个方法和它的参数,也可以是一个Task。
  2. 既然这些方法的形参可以是Task,那么我们理所当然要支持Task的完成通知。
  3. 一组异步操作的返回值如何传递到下一组异步操作 ,对于all方法如何接收前一组异步操作的所有输出参数呢?在上一章里我们通过this.param接收参数,这里对于then,any方法同样如此,因为最终它们只会有一个异步操作的输出参数传递到下一个异步操作,all方法比较特殊,我斟酌好久决定使用this.param[0],this.param[1]... 来接收同组的不同异步操作的输出参数
  4. 当不同状态的Task作为参数传递到另一个Task中时(未开始执行的,正在执行的,执行完成的),应该怎么处理?我们还得支持Task的状态管理。

继续上一章所写的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 })();
View Code

相关文章:

  • 2021-05-20
  • 2021-06-09
  • 2022-12-23
  • 2021-12-18
  • 2022-02-10
  • 2021-11-05
  • 2022-02-28
  • 2021-07-25
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2021-05-31
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案