工作流程拆解
基础原则
- 每一帧为一个函数
- 函数可以返回纯数据,也可以返回另一帧
- 函数按照加入工作流的顺序向下执行
示例
import {Flow} from './lib/flow.gs';
var flow = Flow();
function frame(data, flow){
var nextData = {};
return nextData;
}
// 主流程
flow.push(frame)
flow.push(frame)
flow.push("abc",frame)
flow.push(frame)
flow.push(frame)
//分支流程2
flow.push(frame)
flow.push(frame)
flow.push(frame)
flow.push(frame)
flow.push(frame)
//分支流程1
flow.push(frame)
flow.push(frame)
flow.push(frame)
flow.push(frame)
//其他状态
flow.push(frame)
//开始流程
flow.start();
Flow.gs
请直接拷贝以下代码到 src/lib/flow.gs
文件中去
function frameDescribe(stepIndex, data) {
return {
IS_FRAME: true,
stepIndex,
data: data || {}
}
}
function copy(data) {
return typeof data === 'object' ? JSON.parse(JSON.stringify(data)) : data;
}
export function Flow() {
// 历史栈 index 记录
var stepIndexRecord = [];
// 历史栈 关键帧 记录
var stepFrameRecord = [];
var stepDataRecord = [];
var flowList = [];
var eventFlow = {};
var stepIndex = 0;
var context = {}
function isBack(input) {
return String(input) === "back";
}
function push(keyFrame, fn) {
if (typeof keyFrame === "string") {
flowList.push([fn, keyFrame]);
} else {
flowList.push([keyFrame]);
}
}
function start(data) {
stepIndex = 0;
stepIndexRecord = [];
stepFrameRecord = [];
stepDataRecord = []
next(data !== undefined ? data : {});
}
function next(data) {
// 超出所有步骤
if (stepIndex >= flowList.length) {
stepIndex = 0;
}
var frame = flowList[stepIndex];
if (frame) {
var framefn = frame[0];
// 执行记录
stepIndexRecord.push(stepIndex);
stepFrameRecord.push(frame[1]);
// 数据记录
stepDataRecord.push(copy(data));
// 在当前 flow 中保存 data
context.data = data;
var ret = framefn(data, context);
if (ret && ret.IS_FRAME) {
// 当前 flow 的其他状态
stepIndex = ret.stepIndex;
ret = ret.data;
} else {
stepIndex++;
}
// 正常执行下一步
next(ret);
} else {
throw new Error("not frame index: " + stepIndex);
}
}
function getKeyFrame(frame, data) {
if (frame === undefined) {
throw new Error("frame must be string");
}
if (frame === "FLOW_FIRST") {
return frameDescribe(0, data)
}
if (frame === "FLOW_LAST") {
return frameDescribe(flowList.length - 1, data)
}
for (var i = 0; i < flowList.length; i++) {
if (flowList[i][1] === frame) {
return frameDescribe(i, data)
}
}
throw new Error("not find " + frame + " key");
}
function has(frame) {
try {
getKeyFrame(frame);
return true;
} catch (e) {
return false;
}
}
function getFirstFrame(frame) {
return getKeyFrame("FLOW_FIRST");
}
function getLastFrame(frame) {
return getKeyFrame("FLOW_LAST");
}
function getCurrentFrame(data) {
return frameDescribe(stepIndex, data);
}
// 从新播放当前帧,不会被记录到历史
function replayCurrentFrame(data) {
var nextFrameDescribe = getCurrentFrame(data);
stepIndexRecord = stepIndexRecord.slice(0, -1);
stepFrameRecord = stepFrameRecord.slice(0, -1);
stepDataRecord = stepDataRecord.slice(0, -1);
return nextFrameDescribe;
}
// 删除当前帧,使历史记录中不包含当前帧
function deleteCurrentFrame(){
stepIndexRecord = stepIndexRecord.slice(0, -1);
stepFrameRecord = stepFrameRecord.slice(0, -1);
stepDataRecord = stepDataRecord.slice(0, -1);
}
// 返回上一个来源帧
function back() {
var index = 0;
var data = {}
if (stepIndexRecord.length >= 2) {
// 删除最后一个元素
index = stepIndexRecord[stepIndexRecord.length - 2];
data = stepDataRecord[stepDataRecord.length - 2];
stepIndexRecord = stepIndexRecord.slice(0, -2);
stepFrameRecord = stepFrameRecord.slice(0, -2);
stepDataRecord = stepDataRecord.slice(0, -2);
} else {
stepIndexRecord = [];
stepDataRecord = [];
}
return frameDescribe(index, data);
}
// 返回历史栈里固定的一个关键帧
function backTo(frame, data){
if(frame === undefined){
throw new Error('back to must be frame key')
}
const index = stepFrameRecord.indexOf(frame);
if(index > -1){
var historyData = data || stepDataRecord[index];
stepIndexRecord = stepIndexRecord.slice(0, index);
stepFrameRecord = stepFrameRecord.slice(0, index);
stepDataRecord = stepDataRecord.slice(0, index);
return getKeyFrame(frame, historyData);
};
throw new Error('back to not found :'+ frame)
}
// 从固定帧开始执行
function startFrom(frameDescribe, data) {
if (typeof frameDescribe === "string") {
if (has(frameDescribe)) {
frameDescribe = getKeyFrame(frameDescribe, data);
} else {
throw new Error(frameDescribe + " not found")
}
}
stepIndexRecord = [];
stepFrameRecord = [];
stepDataRecord = [];
stepIndex = frameDescribe.stepIndex;
next(frameDescribe.data);
}
context.push = push;
context.isBack = isBack;
context.start = start;
context.get = getKeyFrame;
context.has = has;
context.back = back;
context.backTo = backTo;
context.getFirstFrame = getFirstFrame;
context.getLastFrame = getLastFrame;
context.getCurrentFrame = getCurrentFrame;
context.replayCurrentFrame = replayCurrentFrame;
context.deleteCurrentFrame = deleteCurrentFrame;
context.restart = start;
context.startFrom = startFrom;
return context;
}
用法
import { Flow } from './lib/flow.gs';
var flow = Flow();
// 状态1
flow.push(function(data, flow){
var code = labor.scan("xxx/lemo.json",{});
// 跳转回上一步
if(flow.isBack(code)){
return flow.back();
}
if(code === "3"){
//跳转到 p3
return flow.get("abc", {})
}
// 跳转到下一个步
return {}
})
// 状态2
flow.push("p3",function(data, flow){
if(true){
// 从第一步重新开始,会清空历史栈
return flow.restart();
}
return {}
})
function fenzhifn(){
// 从xxx流程开始执行
flow.startFrom(flow.get("xxx", {}));
}
// options 用法
flow.push(function(){
const options = {
"分支1":"fenzhifn"
}
var code = labor.scan("xxx/lemo.json",{}, options);
})
// 结束状态处理
flow.push("release", function(){
labor.release();
})
flow.start();
API
Flow()
var flow = Flow();
flow.data
可以获取当前帧的数据。与当前帧第一个参数data完全相等(同一个对象引用)
flow.push(function p1(data, flow){
data === flow.data // true
return {
a:1
}
})
flow.push(function p2(data, flow){
data.a // 1
flow.data.a // 1
})
flow.start();
flow.back()
获得上一帧,并且数据也会回到上一帧的状态
return flow.back()
flow.backTo(key)
获得历史栈中一个关键帧,并返回到这一帧,数据也会同步还原
return flow.backTo("abc")
flow.get(key)
获取一个关键帧,并传递参数
return flow.get("abc", {})
flow.getFirstFrame(data)
获取第一帧
return flow.getFirstFrame({})
flow.getLastFrame(data)
获取最后一帧
return flow.getLastFrame({})
flow.getCurrentFrame(data)
获取当前帧,会增加历史记录。
return flow.getCurrentFrame({})
flow.replayCurrentFrame(data)
重放当前帧,多次重放,不会增加历史记录。通常可以替代getCurrentFrame
return flow.replayCurrentFrame({})
flow.restart(data)
获取第一个帧,并且会重置历史帧记录
return flow.restart({})
flow.isBack(key)
判断当前用户输入是否按了back键
flow.isBack("back") // true
flow.isBack("xxxxxx") // false
flow.startFrom(key, data)
从一个固定位置启动流程
flow.startFrom(flow.get("abx", {}))
// 上下等价,建议用下面的方式
flow.startFrom("abx", {})
flow.start(data)
从第一帧开始启动流程
flow.start({});
flow.push(key, fn)
像flow中添加一帧。
// 添加帧,匿名
flow.push(function(data, flow){})
// 添加个关键帧
flow.push("abc", function(data, flow){});
// 判断关键帧是否存在
flow.has("abc") // true
flow.has(key)
判断关键帧是否存在
flow.has(key)// 返回 true or false
推荐代码组织方式
// api.gs
export testapi(a, b, c){
return xxxx.api(a, b, c)
}
// pages.gs
export scanDeskCode(a, options){
return labor.scan("/xxx/xxx.lemo.json", {a}, options)
}
// start.gs
import {Flow} from './lib/flow.gs';
import * as API from './api.gs';
import * as page from './pages.gs';
var flow = Flow();
flow.push(function(data, flow){
var code = page.scanDeskCode(data.a, {});
if(flow.isBack(code)){
return flow.back();
}
var res = API.testapi(code, data.a);
return res;
})
flow.push("p2",function(){
//...
});
flow.push("p3",function(){
//...
});
// 流程结束
flow.push(function(){
labor.release();
});
// 页面分支处理推荐
// function fenzhi1(){
// 从p3开始继续执行流程
// flow.startFrom('p3', flow.data)
// }
flow.start();