工作流程拆解
基础原则
- 每一帧为一个函数
- 函数可以返回纯数据,也可以返回另一帧
- 函数按照加入工作流的顺序向下执行
示例
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)//分支流程2flow.push(frame)flow.push(frame)flow.push(frame)flow.push(frame)flow.push(frame)//分支流程1flow.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 中保存 datacontext.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();
