• 一步一步实现基于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
  • 相关阅读:
    abcde =(ab+cd)的平方
    求水仙花数
    VS2019 开发 MFC ACtivex (OCX)控件
    简单体验pdfjs,并且隐藏下载、打印等按钮
    体验win10的linux子系统
    nodejs 连接 mysql 查询事务处理
    Linux系統日常運維管理
    hexo豆瓣卡片安裝遇到的坑
    ZooKeeper 是什么与概述,典型用例
    K8S_Kubernetes
  • 原文地址:https://www.cnblogs.com/lihao123/p/3869922.html
Copyright © 2020-2023  润新知