一个只有99行代码的JS流程框架(二)

张镇圳,腾讯Web前端高级工程师,对内部系统前端建设有多年经验,喜欢钻研捣鼓各种前端组件和框架。

导语

前面写了一篇文章,叫《一个只有99行代码的JS流程框架》,虽然该框架基本已经能实现一个流程正常的逻辑流转,但是在分模块应用下还是缺少一定的能力,无法将一个页面中的不同模块很好的连接在一起,于是对之前的框架进行了升级,新增了子流程的概念。

子流程

什么是子流程?在这个升级后的框架里(当然代码已经不止99行了,不要在乎标题),每个步骤不但可以是一个function,还可以引用另一个流程,这个被引用的流程就叫子流程。先看个简单的例子:

flowJS({
    init:function(){
        this.setNext(‘步骤A‘).setNext(‘步骤B‘).setNext(‘步骤C‘);
        this.next();
    },
    ‘步骤A‘:function(){
        this.next();
    },
    ‘步骤B‘:{
    init:function(){
        this.setNext(‘子步骤B1‘).setNext(‘子步骤B2‘).setNext(‘子步骤B3‘);
        this.next();
        },
        ‘子步骤B1‘:function(){
        this.next();
        },
        ‘子步骤B2‘:function(){
            this.next();
        },
        ‘子步骤B3‘:function(){
        this.parent.next();
    }
    },
    ‘步骤C‘:function(){
        console.log(‘执行 步骤C‘);
    console.log(‘当前流程运行的轨迹:‘);
    console.log(flowJS.trace);
    }
});

上面这个例子中,步骤B对应的对象就是子流程。

还可以有另一种写法,也是对分模块应用的更好的实现:

/*定义子流程*/
flowJS(‘子流程B‘, {
    init:function(){
        this.setNext(‘子步骤B1‘).setNext(‘子步骤B2‘).setNext(‘子步骤B3‘);
        this.next();
    },
    ‘子步骤B1‘:function(){
        this.next();
    },
    ‘子步骤B2‘:function(){
        this.next();
    },
    ‘子步骤B3‘:function(){
        this.parent.next();
    }
});
/*父流程*/
flowJS({
    init:function(){
        this.setNext(‘步骤A‘).setNext(‘步骤B‘).setNext(‘步骤C‘);
        this.next();
    },
    ‘步骤A‘:function(){
        this.next();
    },
    ‘步骤B‘:‘子流程B‘,
    ‘步骤C‘:function(){
        console.log(‘执行 步骤C‘);
    console.log(‘当前流程运行的轨迹:‘);
    console.log(flowJS.trace);
    }
});

可以看到,父流程的 步骤B 引用了前面定义的 子流程B,这样对于一些公共的流程逻辑就可以单独抽取出去作为子流程,被其他父流程引用。而子流程与父流程的交互,我们可以在代码中通过 this.parent 来实现。

在子流程的每一步中都可以获取 this.parent,得到的是当前子流程对应的步骤,这个步骤跟其他步骤一样也具有同样的API(详见上一篇文章《一个只有99行代码的JS流程框架》对步骤API的介绍)。

另外,需要说明的一点:这次的升级,并没有对流程步骤的API做改变,仅仅是引入了子流程的使用方式,其实就是定义子流程,然后引用子流程,接着就是父流程和子流程之间的交互

同样,按照规矩,贴上code(例子的序号接上前篇文章的序号,从10开始)

最简单的子流程使用方法

flowJS({
    init:function(){
    console.log(‘执行 init‘);
        this.setNext(‘步骤A‘).setNext(‘步骤B‘).setNext(‘步骤C‘);
        this.next();
    },
    ‘步骤A‘:function(){
        console.log(‘执行 步骤A‘);
        this.next();
    },
    ‘步骤B‘:{
    init:function(){
        console.log(‘执行 子步骤B init‘);
        this.setNext(‘子步骤B1‘).setNext(‘子步骤B2‘).setNext(‘子步骤B3‘);
        this.next();
        },
        ‘子步骤B1‘:function(){
            console.log(‘执行 子步骤B1‘);
            this.next();
        },
        ‘子步骤B2‘:function(){
            console.log(‘执行 子步骤B2‘);
            console.log(‘上一步 :‘+this.getPrev()); //打印:子步骤B1
            console.log(‘当前步 :‘+this.getCurr()); //打印:子步骤B2
        console.log(‘下一步 :‘+this.getNext()); //打印:子步骤B3
        this.next();
        },
        ‘子步骤B3‘:function(){
            console.log(‘执行 子步骤B3‘);
            this.parent.next();
        }
    },
    ‘步骤C‘:function(){
        console.log(‘执行 步骤C‘);
    console.log(‘当前流程运行的轨迹:‘);
    console.log(flowJS.trace);
    }
});

执行结果:

子流程和父流程 通过 this.parent 进行交互

flowJS({
    init:function(){
    console.log(‘执行 init‘);
        this.setNext(‘步骤A‘).setNext(‘步骤B‘).setNext(‘步骤C‘);
        this.next();
    },
    ‘步骤A‘:function(){
        console.log(‘执行 步骤A‘);
    this.nextData({name1:‘value1‘});
    this.flowData({name2:‘value2‘});
        this.next();
    },
    ‘步骤B‘:{
    init:function(){
        console.log(‘执行 子步骤B init‘);
        this.setNext(‘子步骤B1‘).setNext(‘子步骤B2‘).setNext(‘子步骤B3‘);
        this.next();
    },
    ‘子步骤B1‘:function(){
        console.log(‘执行 子步骤B1‘);
        this.nextData({name3:‘value3‘});
        this.flowData({name4:‘value4‘});
        this.next();
    },
    ‘子步骤B2‘:function(){
        console.log(‘执行 子步骤B2‘);
        console.log(‘父步骤的上一步 :‘+this.parent.getPrev());//打印:步骤A
        console.log(‘父步骤的步骤名 :‘+this.parent.getCurr());//打印:步骤B
        console.log(‘父步骤的下一步 :‘+this.parent.getNext());//打印:步骤C
        console.log(‘父步骤的数据:‘);
        console.log(this.parent.stepData());//打印:Object {name1: "value1"}
        console.log(this.parent.flowData());//打印:Object {name2: "value2"}
        console.log(‘上一步 :‘+this.getPrev());//打印:子步骤B1
        console.log(‘当前步 :‘+this.getCurr());//打印:子步骤B2
        console.log(‘下一步 :‘+this.getNext());//打印:子步骤B3
        console.log(‘当前步的数据:‘);
        console.log(this.stepData());//打印:Object {name3: "value3"}
        console.log(this.flowData());//打印:Object {name4: "value4"}
        this.next();
    },
    ‘子步骤B3‘:function(){
        console.log(‘执行 子步骤B3‘);
        this.parent.nextData({name5:‘value5‘});
        this.parent.flowData({name6:‘value6‘});
        this.parent.next();
    }
    },
    ‘步骤C‘:function(){
        console.log(‘执行 步骤C‘);
    console.log(this.stepData());//打印:Object {name5: "value5"}
    console.log(this.flowData());//打印:Object {name2: "value2", name6: "value6"}
    console.log(‘当前流程运行的轨迹:‘);
    console.log(flowJS.trace);
    }
});

执行结果:

多个子流程并行执行

flowJS({
    init:function(){
    console.log(‘执行 init‘);
        this.setNext(‘步骤A‘).setNext([‘步骤B‘, ‘步骤C‘]).setNext(‘步骤D‘);
        this.next();
    },
    ‘步骤A‘:function(){
        console.log(‘执行 步骤A‘);
        this.next();
    },
    ‘步骤B‘:{
    init:function(){
        console.log(‘执行 子步骤B init‘);
        this.setNext(‘子步骤B1‘).setNext(‘子步骤B2‘).setNext(‘子步骤B3‘);
        this.next();
        },
        ‘子步骤B1‘:function(){
        console.log(‘执行 子步骤B1‘);
        this.next();
    },
    ‘子步骤B2‘:function(){
        console.log(‘执行 子步骤B2‘);
        this.next();
    },
    ‘子步骤B3‘:function(){
        var self = this;
            //这里打印的时间和 子步骤C3 的时间一样
        console.log(‘执行 子步骤B3 时间:‘ + new Date().getSeconds());
        setTimeout(function(){
            self.parent.next();
        }, 2000);
    }
    },
    ‘步骤C‘:{
    init:function(){
        console.log(‘执行 子步骤C init‘);
        this.setNext(‘子步骤C1‘).setNext(‘子步骤C2‘).setNext(‘子步骤C3‘);
        this.next();
    },
        ‘子步骤C1‘:function(){
        console.log(‘执行 子步骤C1‘);
        this.next();
    },
    ‘子步骤C2‘:function(){
        console.log(‘执行 子步骤C2‘);
        this.next();
    },
    ‘子步骤C3‘:function(){
        var self = this;
            //这里打印的时间和 子步骤B3 的时间一样
        console.log(‘执行 子步骤C3 时间:‘ + new Date().getSeconds());
        setTimeout(function(){
            self.parent.next();
        }, 2000);
    }
    },
    ‘步骤D‘:function(){
        //这里打印的时间比上面的子流程的时间晚2秒,因为两个子流程是并行执行的
        console.log(‘执行 步骤D 时间:‘ + new Date().getSeconds());
    console.log(‘当前流程运行的轨迹:‘);
    console.log(flowJS.trace);
    }
});

执行结果:

定义子流程和引用子流程

flowJS(‘子流程A‘, {
    init:function(){
        this.next(‘子步骤A1‘);
    },
    ‘子步骤A1‘:function(){
        console.log(‘执行 子步骤A1‘);
        console.log(‘当前步骤:‘+this.getCurr());//打印:子步骤A1
        console.log(‘父步骤:‘+this.parent.getCurr());//打印:步骤A
        this.parent.next();
    }
});
flowJS(‘子流程B‘, {
    init:function(){
        console.log(‘执行 子步骤B init‘);
        this.setNext(‘子步骤B1‘).setNext(‘子步骤B2‘).setNext(‘子步骤B3‘);
        this.next();
    },
    ‘子步骤B1‘:function(){
        console.log(‘执行 子步骤B1‘);
        this.next();
    },
    ‘子步骤B2‘:function(){
        console.log(‘执行 子步骤B2‘);
        this.next();
    },
    ‘子步骤B3‘:function(){
        console.log(‘执行 子步骤B3‘);
        console.log(‘当前步骤:‘+this.getCurr());//打印:子步骤B3
        console.log(‘父步骤:‘+this.parent.getCurr());//打印:步骤B
        this.parent.next();
    }
});
flowJS(‘子流程C‘, {
    init:function(){
        console.log(‘执行 子步骤C init‘);
        this.setNext(‘子步骤C1‘).setNext(‘子步骤C2‘).setNext(‘子步骤C3‘);
        this.next();
    },
    ‘子步骤C1‘:function(){
        console.log(‘执行 子步骤C1‘);
        this.next();
    },
    ‘子步骤C2‘:function(){
        console.log(‘执行 子步骤C2‘);
        this.next();
    },
    ‘子步骤C3‘:function(){
        console.log(‘执行 子步骤C3‘);
        console.log(‘当前步骤:‘+this.getCurr());//打印:子步骤C3
        console.log(‘父步骤:‘+this.parent.getCurr());//打印:步骤C
        this.parent.next();
    }
});
flowJS({
    init:function(){
    console.log(‘执行 init‘);
        this.setNext(‘步骤A‘).setNext([‘步骤B‘, ‘步骤C‘]).setNext(‘步骤D‘);
        this.next();
    },
    ‘步骤A‘:‘子流程A‘,
    ‘步骤B‘:‘子流程B‘,
    ‘步骤C‘:‘子流程C‘,
    ‘步骤D‘:function(){
    console.log(‘当前流程运行的轨迹:‘);
    console.log(flowJS.trace);
    }
});

* 执行结果:*

从上面几个例子可以看到,子流程和父流程之间的信息交互非常简单,其实就是通过this.parent来获取到父步骤,通过父步骤来获取和传递数据,因此也能让这个流程框架拥有更大能力来适应更多的应用场景。

为了方便交流学习,上面例子完整代码可通过附件下载,最后同样贴上框架源码:

相关阅读

一个只有99行代码的JS流程框架 (一)

JavaScriptCore全面解析 (上篇)

JavaScriptCore全面解析 (下篇)



此文已由作者授权腾讯云技术社区发布,转载请注明文章出处

原文链接:https://www.qcloud.com/community/article/318172

获取更多腾讯海量技术实践干货,欢迎大家前往腾讯云技术社区

时间: 2024-08-26 03:25:51

一个只有99行代码的JS流程框架(二)的相关文章

一个只有99行代码的JS流程框架

最近一直在想一个问题,如何能让js代码写起来更语义化和更具有可读性. 上周末的时候突发奇想,当代码在运行的时候,其实跟我们做事情是类似的,都是做完一步接着下一步,并且这些事情有些是可规划的,有些是需要做完该步才知道下一步该做什么.想到这里一个js框架雏形在我大脑中慢慢形成,暂且命名为flowJS. 接着说说这个框架应该有哪些API? 1.可以预先规划好流程的每一步,如this.setNext('步骤A').setNext('步骤B')-- 2.可以在任何一步决定下一步做什么,如 this.set

一个女大学生的代码学习之路(二)

首先说一下,写这种文章是由于我在四月四日晚上,在手动搭建自己的第一个ssh项目的时候,遇到了一个配置的问题,怎么解决也弄不好,当时是四号晚上九点,我看了一眼表,我就想两个小时之内,我要是能搞定就算行了,但是其实,我搞到三点才OK(凌晨),那时候已经是五号了,转天是一家子去扫墓的时候,结果我居然以这种一个理由没有去,理由是我太累了么?我只是就是搭了一个架子,就是由于我的包太混乱了,导致不兼容,所以tomcat总也不启动,你可能认为好笑,这么简单一个问题怎么就费这多多时间呢,但是作为一个刚接触三框架

4行代码实现js模板引擎

在平时编码中,经常要做拼接字符串的工作,如把json数据用HTML展示出来,以往字符串拼接与逻辑混在在一起会让代码晦涩不堪,加大了多人协作与维护的成本.而采用前端模板机制就能很好的解决这个问题. 精妙的 tmpl 前端模板类开源的不少,但最属 jQuery 作者 John Resig 开发的 “javascript micro templating” 最为精妙,寥寥几笔便实现了模板引擎核心功能. 它的介绍与使用方式请看作者博客:http://ejohn.org/blog/javascript-m

蛋疼之作:99行代码的2048

基于Python和numpy,自带基于Tk最简仿原生2048配色的图形界面.文件代码行数(Physical LOC)一共99,因为是Python所以逻辑行数未必. Not Pythonic, PEP8 is not followed. 对2048游戏规则的理解: 1) 方块合并时从滑动所指方向开始合并,合并不递归,比如: | |2|2|2|向右滑动后,最右两块合并,变为| | |2|4| |2|2|2|2|向右滑动后,最右和最左两块分别合并,变为| | |4|4| 2) 每次滑动后如果方块发生了

30 行代码实现 JS 中的 MVC

一连串的名字走马观花式的出现和更迭,它们中一些已经渐渐淡出了大家的视野,一些还在迅速茁壮成长,一些则已经在特定的生态环境中独当一面舍我其谁.但不论如何,MVC已经并将持续深刻地影响前端工程师们的思维方式和工作方法. 很多讲解MVC的例子都从一个具体的框架的某个概念入手,比如Backbone的collection或AngularJS中model,这当然不失为一个好办法.但框架之所以是框架,而不是类库(jQuery)或者工具集(Underscore),就是因为它们的背后有着众多优秀的设计理念和最佳实

基本上,把switch,用设计模式代替,肯定是bug和过度设计。想想,本来修改一个文件几行代码可以解决的问题,变成修改3-6个类才能实现一样的功能。不是傻是什么?

那些迷信设计模式的人,来修改一下这个方法吧.看看你最终的代码膨胀为几倍... public virtual PasswordChangeResult ChangePassword(ChangePasswordRequest request) { if (request == null) throw new ArgumentNullException("request"); var result = new PasswordChangeResult(); if (String.IsNul

你相信么,只需一个函数5行JS代码即可在Javascript中实现完整的AOP功能

你相信么,只需一个函数5行JS代码即可在Javascript中实现完整的AOP功能, 你相信么,在JavaScript只需一个函数5行代码即可实现完整的面向方面AOP编程功能.这5行代码的功能包括: 无限层次的函数无害拦截 函数执行前拦截 检查函数的参数值 重新设定函数的参数值 函数执行后拦截 检查函数执行后的返回结果 重新设定函数的返回结果 虽然动态函数式语言的效率是一个存在的问题,但是对于它的高度灵活性,简直让人令人惊叹不已,剧赞. 这个小小的函数源自于和爱明兄的一次讨论:在javascri

JS流程设计器(二)

此代码接着JS流程设计器(一)续写,该代码主要是怎么将设计好的流程图用最基本字符串的方式传到后台,再由后台字符串的方式重新显示在页面上. 1 //转化成后端的方法 2 function changebe(firstbegin){ 3 for(var x=0;x<shapeobjjson.shapeobj.length;x++){ 4 if(shapeobjjson.shapeobj[x].id==firstbegin){ 5 if(firstbegin.indexOf("begin-cir

分享一个开源的JavaScript统计图表库,40行代码实现专业统计图表

提升程序员工作效率的工具/技巧推荐系列 推荐一个功能强大的文件搜索工具SearchMyFiles 介绍一个好用的免费流程图和UML绘制软件-Diagram Designer 介绍Windows任务管理器的替代者-Process Explorer 介绍一个强大的磁盘空间检测工具Space Sniffer 如何在电脑上比较两个相似文件的差异 程序员工作效率提升系列-推荐一个JSON文件查看和修改的小工具 将Chrome调试器里的JavaScript变量保存成本地JSON文件 这可能是史上最简单易用的