首页 > 代码库 > 一步一步实现基于Task的Promise库(五)waitFor和waitForAny的实现

一步一步实现基于Task的Promise库(五)waitFor和waitForAny的实现

在实现waitFor方法之前,我们先要搞明白下面这些问题:

1. waitFor方法的形参有限制吗?

    没有!如果形参是Task类型,不应该启动Task,如果是function类型,会执行方法.所以waitFor的使用场景应该是waitFor(task1,task2),并且task1,2不知道何时启动(比如是用户点击界面按钮来启动)

2. 关于参数的传递。

1 var taskExp_0 = new Task(readFile, "123.txt");
2 var taskExp_1 = new Task(readFile, "aa.txt").start();
3 var taskExp_2 = new Task(readFile, "bb.txt");
4 var taskExp_3 = new Task(taskExp_0).waitFor(taskExp_1, taskExp_2).then(writeFile).start();
5 //taskExp_2模拟不知何时运行的Task,请在控制台运行下面代码
6 //taskExp_2.start();

上面例子中,taskExp_1,taskExp_2不接收taskExp_0的输出参数,我们希望writeFile可以通过this.Param[0],[1],[2]分别接收taskExp_0,taskExp_1,taskExp_2的输出参数。

明确了这样的设计后,下面是Task.js的实现细节和相关demo,有关waitFor和waitForAny的实现请看注释:

  1 <!DOCTYPE html>
  2 <html>
  3 <head>
  4     <title></title>
  5 </head>
  6 <body>
  7     <script type="text/javascript">
  8         //promise
  9         //读取文件的原始内容
 10         var uploadFile = function (fileName) {
 11             var _this = this;
 12             window.setTimeout(function () {
 13                 console.log("uploadFile ‘" + fileName + "‘ complete.");
 14                 _this.end(fileName);
 15             }, 1800);
 16         };
 17         //读取文件的原始内容
 18         var readFile = function (fileName) {
 19             _fileName = fileName || this.param;
 20             var _this = this;
 21             window.setTimeout(function () {
 22                 var rawContent = "xxxxxxxx (" + _fileName + ")";
 23                 console.log("read ‘" + _fileName + "‘ complete. rawContent is " + rawContent);
 24                 _this.end(rawContent);
 25             }, 1000);
 26         };
 27         //请求服务器来解析原始内容,得到真正的内容
 28         var resolveFile = function (serverUrl) {
 29             var _this = this;
 30             var rawContent = _this.param;
 31             window.setTimeout(function () {
 32                 var realContent = "Greeting (" + serverUrl + ")";
 33                 console.log("resolve file complete. realContent is " + realContent);
 34                 _this.end(realContent);
 35             }, 1500);
 36         };
 37         //把真正的内容写入文件
 38         var writeFile = function (fileName) {
 39             var _this = this;
 40             window.setTimeout(function () {
 41                 console.log("writeBack1 param[0] is " + _this.param[0] + " ;param[1] is " + _this.param[1]);
 42                 _this.end();
 43             }, 2000);
 44         };
 45         var sendMail = function () {
 46             var _this = this;
 47             window.setTimeout(function () {
 48                 console.log("sendMail finished");
 49                 _this.end();
 50             }, 1000);
 51         };
 52 
 53         (function() {
 54             var isFunction = function (target) {
 55                 return target instanceof Function;
 56             };
 57             var isArray = function (target) {
 58                 return target instanceof Array;
 59             };
 60 
 61             //自定义事件管理(代码摘抄自http://www.cnblogs.com/dolphinX/p/3254017.html)
 62             var EventManager = function () {
 63                 this.handlers = {};
 64             };
 65             EventManager.prototype = {
 66                 constructor: EventManager,
 67                 addHandler: function (type, handler) {
 68                     if (typeof this.handlers[type] == ‘undefined‘) {
 69                         this.handlers[type] = new Array();
 70                     }
 71                     this.handlers[type].push(handler);
 72                 },
 73                 removeHandler: function (type, handler) {
 74                     if (this.handlers[type] instanceof Array) {
 75                         var handlers = this.handlers[type];
 76                         for (var i = 0; i < handlers.length; i++) {
 77                             if (handler[i] == handler) {
 78                                 handlers.splice(i, 1);
 79                                 break;
 80                             }
 81                         }
 82                     }
 83                 },
 84                 trigger: function (type, event) {
 85                     /*
 86                     if(!event.target){
 87                         event.target = this;
 88                     }
 89                     */
 90                     if(this.handlers[type] instanceof Array){
 91                         var handlers = this.handlers[type];
 92                         for(var i=0; i<handlers.length; i++){
 93                             handlers[i](event);
 94                         }
 95                     }
 96                 }
 97             };
 98 
 99             //所有检测条件返回{result: bool, output: obj}
100             var Condition = {
101                 then: function(target){
102                     return {result: !!target[0], output: target[0].value};
103                 },
104                 all: function(target){
105                     var output = [];
106                     for(var i=0; i<target.length; i++){
107                         if(target[i]){
108                             output.push(target[i].value);
109                         }
110                         else{
111                             return {result: false};
112                         }
113                     }
114                     return {result: true, output: output};
115                 },
116                 any: function(target){
117                     for(var i=0; i<target.length; i++){
118                         if(target[i]){
119                             return {result: true, output: target[i].value};
120                         }
121                     }
122                     return {result: false};
123                 }
124             };
125 
126             //option:{
127             //   autoStart: bool,   //自动启动
128             //   keepInputParams: bool   //是否把上一步传递过来的input传递给它的下一步
129             //}
130             //finishedCallback 表示WorkItem满足条件并结束的回调
131             var WorkItem = function(arrayArgs, finishedCallback, option){
132                 var _subItems = [];
133                 var _rawOutputParams = [];
134                 //完成WorkItem的检测条件
135                 var _condition;
136                 var _input;
137                 option = option || {};
138                 //增加一个bool类型的属性autoStart,默认值为true,表示当调用_startSubItem时是否自动执行subItem(当subItem是task时根据autoStart参数来执行task,如果subItem是方法时,不管autoStart是什么都会执行)
139                 option.autoStart = option.autoStart !== false;
140                 //是否把上一步传递过来的input传递给它的下一步,默认false
141                 option.keepInput = option.keepInput === true;
142 
143                 var _checkFunc = function(args){
144                     if(isFunction(args[0])){
145                         if(args.length == 2 && isArray(args[1])){
146                             _subItems.push({‘isFunc‘: true, ‘func‘: args[0], ‘args‘: args[1]});
147                         }
148                         else{
149                             _subItems.push({‘isFunc‘: true, ‘func‘: args[0], ‘args‘: args.slice(1)});
150                         }
151                         return true;
152                     }
153                     return false;
154                 };
155                 var _checkTask = function(task){
156                     if(task instanceof Task){
157                         _subItems.push({‘isFunc‘: false, ‘task‘: task});
158                     }
159                 };
160                 if(!_checkFunc(arrayArgs)){
161                     for(var i=0; i<arrayArgs.length; i++){
162                         if(!_checkFunc(arrayArgs[i])){
163                             _checkTask(arrayArgs[i]);
164                         }
165                     }
166                 }
167                 _rawOutputParams.length = _subItems.length;
168 
169                 var _startSubItem = function(subItemIndex){
170                     var subItem = _subItems[subItemIndex];
171                     if(subItem.isFunc){
172                         var workItemCxt = _getSubItemContext(subItemIndex);
173                         subItem.func.apply(workItemCxt, subItem.args);
174                     }
175                     else{
176                         if(subItem.task.getStatus() == TaskStatus.finished){
177                             _endSubItem(subItem.task.getOutput(), subItemIndex)
178                         }
179                         else{
180                             subItem.task.finished(function(output){
181                                 _endSubItem(output, subItemIndex);
182                             });
183                             if(option.autoStart){
184                                 subItem.task.start(_input);
185                             }
186                         }
187                     }
188                 };
189                 var _endSubItem = function(output, index){
190                     _rawOutputParams[index] = {
191                         ‘value‘: output
192                     };
193                     var testResult = Condition[_condition](_rawOutputParams);
194                     if(testResult.result){
195                         _onFinishedCallback(testResult.output);
196                     }
197                 };
198                 var _merge = function(target, data){
199                     if(data){
200                         if(isArray(data)){
201                             for(var i=0; i<data.length; i++){
202                                 target.push(data[i]);
203                             }
204                         }
205                         else{
206                             target.push(data);
207                         }
208                     }
209                 };
210                 var _onFinishedCallback = function(output){
211                     //如果需要保留输入参数,那么需要对输入和输出参数来一个合并
212                     if(option.keepInput){
213                         var result = [];
214                         _merge(result, _input);
215                         _merge(result, output);
216                         if(result.length == 0){
217                             output = undefined;
218                         }
219                         else{
220                             output = result;
221                         }
222                     }
223                     finishedCallback(output)
224                 };
225                 var _getSubItemContext = function(index){
226                     return {
227                         param: _input,
228                         end: function(output){
229                             _endSubItem(output, index);
230                         }
231                     };
232                 };
233 
234                 this.setCondition = function(condition){
235                     _condition = condition;
236                 };
237                 this.start = function(input){
238                     _input = input;
239                     for(var i=0; i<_subItems.length; i++){
240                         _startSubItem(i);
241                     }
242                 };
243             };
244             var ConditionWorkItem = function(finishedCallback){
245                 this.start = function(input){
246                     finishedCallback(input);
247                 };
248             };
249 
250             var TaskStatus = {
251                 //未开始
252                 pending: 0,
253                 //正在进行
254                 doing: 1,
255                 //已完成
256                 finished: 2
257             };
258 
259             window.Task = function(){
260                 var _status = TaskStatus.pending;
261                 var _wItemQueue = [], _currentItem;
262                 var _eventManager = new EventManager();
263                 var _output;
264                 //设置_wItemQueue队列的最后一个WorkItem的完成条件
265                 var _setLastItemCondition = function(condition){
266                     if(condition != null){
267                         var last = _wItemQueue[_wItemQueue.length - 1];
268                         //因为ConditionWorkItem是没有setCondition方法的(它也不需要判断条件),所以有这个if
269                         if(last.setCondition){
270                             last.setCondition(condition);
271                         }
272                     }
273                 };
274                 var _initWorkItem = function(condition, args, option){
275                     _setLastItemCondition(condition);
276                     var item;
277                     if(args.length == 0){
278                         item = new ConditionWorkItem(_finishCallback);
279                     }
280                     else{
281                         var arrayArgs = [];
282                         for(var i=0; i<args.length; i++){
283                             arrayArgs[i] = args[i];
284                         }
285                         item = new WorkItem(arrayArgs, _finishCallback, option);
286                     }
287                     _wItemQueue.push(item);
288                     return item;
289                 };
290                 //WorkItem完成的回调
291                 var _finishCallback = function(output){
292                     var next = _getCurNextItem();
293                     if(next){
294                         //如果有下一个WorkItem,就start它
295                         _currentItem = next;
296                         _currentItem.start(output);
297                     }
298                     else{
299                         //如果没有就通知Task结束
300                         _status = TaskStatus.finished;
301                         _output = output;
302                         _eventManager.trigger("finish", output);
303                     }
304                 };
305                 var _getCurNextItem = function(){
306                     var i=0;
307                     for(; i<_wItemQueue.length; i++){
308                         if(_currentItem == _wItemQueue[i]){
309                             break;
310                         }
311                     }
312                     return _wItemQueue[i + 1];
313                 };
314                 _currentItem = _initWorkItem(null, arguments);
315 
316                 this.getStatus = function(){
317                     return _status;
318                 };
319                 this.getOutput = function(){
320                     return _output;
321                 };
322                 this.finished = function(callback){
323                     if(callback){
324                         _eventManager.addHandler("finish", callback);
325                     }
326                 };
327                 this.start = function(input){
328                     if(_status == TaskStatus.pending){
329                         _status = TaskStatus.doing;
330                         //start的时候给最后一个WorkItem设置then条件
331                         _setLastItemCondition("then");
332                         _currentItem.start(input);
333                     }
334                     return this;
335                 };
336                 this.waitFor = function(){
337                     //先初始化一个不会自启动的WorkItem,并且这个WorkItem把上一步传递过来的input传递给它的下一步
338                     //进入这个WorkItem的条件是then,如果你想要进入条件是all,可以这样 xxxxx.all().waitFor(task1).xxxxx
339                     _initWorkItem("then", arguments, {autoStart: false, keepInput: true});
340                     //最后调用all()表示这个WorkItem里面的所有子Item必须都完成才能继续下一步
341                     return this.all();
342                 };
343                 this.waitForAny = function(){
344                     _initWorkItem("then", arguments, {autoStart: false, keepInput: true});
345                     //前面和waitFor的逻辑一样,最后调用any()表示这个WorkItem里面的所有子Item完成其中一个就可以进入下一步
346                     return this.any();
347                 };
348                 this.then = function(){
349                     _initWorkItem(‘then‘, arguments);
350                     return this;
351                 };
352                 this.all = function(){
353                     _initWorkItem(‘all‘, arguments);
354                     return this;
355                 };
356                 this.any = function(){
357                     _initWorkItem(‘any‘, arguments);
358                     return this;
359                 };
360             };
361         })();
362 
363         var taskExp_1 = new Task(readFile, "aa.txt").then(resolveFile, "/service/fileResolve.ashx?file=aa.txt");
364         var taskExp_2 = new Task(uploadFile, "bb.txt").then(readFile, "bb.txt").then(resolveFile, "/service/fileResolve.ashx?file=bb.txt");
365         var taskExp_3 = new Task(taskExp_1).waitFor(taskExp_2).then(writeFile, ["cc.txt"]).then(sendMail).start();
366         //taskExp_2模拟不知何时运行的Task,请在控制台运行下面代码
367         //taskExp_2.start();
368 
369     </script>
370 </body>
371 </html>
View Code

 

一步一步实现基于Task的Promise库(五)waitFor和waitForAny的实现