seo诊断站长,wordpress 4.5.4 json,凡科建站的优缺点,海口建站模板厂家1. 前置说明
这次再说明下自己对状态图的看法。状态图虽然有非常多的优势#xff08;参考上篇文章#xff09;#xff0c;如果你想使用#xff0c;关于是否对整个旧项目进行全量状态图化#xff0c;这里给一个适应范围是#xff1a;项目中复杂的部分进行状态图建模是非常…
1. 前置说明
这次再说明下自己对状态图的看法。状态图虽然有非常多的优势参考上篇文章如果你想使用关于是否对整个旧项目进行全量状态图化这里给一个适应范围是项目中复杂的部分进行状态图建模是非常合适的。如果你有精力是可以尝试对整个项目进行状态图化的。
1.1 状态图
再回顾一下什么是状态图。 状态图的前身是状态机FSMFSM 使用过程中会暴露一些问题如
状态爆炸层次表达能力弱
项目复杂起来到后期 FSM 会很难维护。
针对这些问题计算机科学家 David Harel 在 1984 年对 FSM 进行了扩展发明了 状态图SC来解决 FSM 中的问题。论文地址
SC 不仅仅是更好的可视化了 FSM而且它是可执行的。现在的大多数状态机工具库更确切的说应该是状态图工具库。
SC 定义为一个分层有向图STRInOut比 FSM 多了一个 ROrthogonal 正交的概念。
SC 设计了一套非常复杂且非常精确的符号系统增强了结构层次的表达能力和有向图的连通表达能力。目前也是 UML 的首选控制模型。
1.2 SCXML
SCXML 全称 State Chart XML用于控制抽象的状态机表示法。
SCXML 是基于上面说的 David Harel 状态图 和 CCXMLCall Control eXtensible Markup Language 进行扩展的一套规范。
从 2005 年到 2015 年经历 10 年定制的规范成为 W3C 推荐规范。目前大部分编程语言的状态机工具都是基于此规范实施的。
1.3 XState
XState 是一个前端的状态图工具库由微软工程师 David Khourshid 开发。目前是前端状态机里面 Star 最多的本人体验下来感觉也很不错本人很高兴在此仓库贡献了 14.7k 行 。下图是 XState Github Star 记录 2. 组织说明
XState 的文档写的并不是很好懂很多概念跳来跳去当然 大多数国外的文档都有这种问题作者肯定很想表达清楚但并不容易做如果读者对状态机没有概念突如其来的一堆新的概念会让你措手不及学习曲线剧增使用上也不知该如何下手。
如果想要对这些概念有更好的认识和组织那用 SCXML 和 XState 去对照着看或许是比较合适的。
2.1 SCXML 的组织
主要有以下部分
核心 scxmlstatetransitioninitialparallelfinalhistoryonentryonexit 可执行内容 raiseforeachlogifelseifelse 数据模型和数据操作 datamodeldatacontentparamdonedatascriptassign 外部通讯 sendcancelinvokefinalize
2.2 XState 的组织
主要有以下部分
MachineStateState NodeEventTransitionParallel StateFinal StateHistory StateEffects InvokeActions sendraiserespondforwardToescalatelogchoosepureassign Activities ContextGuardDelayInterpretIdentifyActorModel
3. 对应关系
下面以 SCXML 为主线去做对应描述。
3.1 核心元素
按照 SCXML 的分类先从核心部分的元素进行对应说明。
3.1.1 scxml
scxml最外层的状态机包裹元素携带版本信息状态机是由它的 children 组成的。
属性字段描述如下
名称必填属性约束类型默认值有效值描述initialfalsenoneIDREFSnone合法的状态规范状态机的初始状态的 id。如果未指定则默认初始状态是文档顺序中的第一个子状态namefalsenoneNMTOKENnone任何有效的NMTOKEN此状态机的名称。它纯粹是为了提供信息xmlnstruenoneURInonehttp://www.w3.org/2005/07/scxmlversiontruenonedecimalnone必须 “1.0”datamodelfalsenoneNMTOKENplatform-specific“null”, “ecmascript”, “xpath” 或者其他平台定义的值本文档所需的数据模型。 “null”表示 Null 数据模型“ecmascript”表示 ECMAScript 数据模型“xpath”表示 XPath 数据模型bindingfalsenoneenum“early”“early”, “late”要使用的数据绑定
children 可以包含
stateparallelfinaldatamodelscript
对应 XState 是 MachineMachine 的部分属性描述如下详情
{id: ,initial: ,context: {},states: {}
}3.1.2 state
state用来描述状态机中的状态。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxmlstate id状态A/
/scxml属性字段描述如下
名称必填属性约束类型默认值有效值描述idfalsenoneIDnone状态 IDinitialfalse不得与 元素一起指定。绝不能以原子状态出现。IDREFSnone此状态的默认初始状态
children 可以包含
onentryonexittransitioninitialstateparallelfinalhistorydatamodelinvoke
对应XState 的 State Node。不过 State Node 是一个 SCXML 多个元素组成的一个属性。由 state、initial、parallel、final、history组成。
State Node 的部分属性描述如下详情
{id: ,states: {},invoke: {},on: {},onEntry: {},onExit: {},onDone: {},always: {},after: {},tags: [],type:
}示例
Machine({id: 状态机,states: {状态A: {id: 状态A,},},
})3.1.3 transition
状态之间进行转换。由事件触发通过条件判断后进行转换。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxmlstate id打开transition cond_event.data1 event点击 target关闭 //statestate id关闭 /
/scxml属性字段描述如下
名称必填属性约束类型默认值有效值描述eventfalseEventsTypes.datatypenone以空格分隔的事件描述符列表触发此转换的事件指示符列表condfalseBoolean expression‘true’布尔表达式转换条件targetfalseIDREFSnone要跳转到的状态要转换到的状态或并行区域的标识符typefalseenum“external”“internal” “external”确定目标状态是来自于内部转换还是外部转换
children 可以包含 可执行内容。
对应XState 的 Event、Transition、Guard。部分属性描述如下详情
{on: {: {},*: {},自定义事件: {target: 目标状态,cond: 条件判断,actions: 可执行内容,in: 只能从这个状态过来,internal: 内部转换,meta: {},description: }}
}示例
Machine({id: 状态机,states: {打开: {on: {点击: {target: 关闭,cond: (ctx, event) event.data 1,},},},关闭: {},},
});3.1.4 initial
initial表示复杂 元素即包含子 或 元素的元素的默认初始状态。并不是一个状态只是一个指向状态的作用。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxmlstate id打开initialtransition target写入 //initialstate id写入 /state id读取 //state
/scxml必须和 transition 一起使用进行状态指定。
children 包含 transition。
XState 可以直接在 State Node 的 initail 进行指定实现。
示例
Machine({id: 状态机,states: {打开: {initial: 读取,states: {读取: {},写入: {},},},},
});3.1.5 parallel
该元素表示一个状态其子项并行执行。当父元素处于活动状态时子项同时处于活动状态。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxmlparallel id网盘state id写入 /state id读取 //parallel
/scxml属性字段描述如下
名称必填属性约束类型默认值有效值描述idfalsenoneIDnoneXML Schema 中定义的有效 id状态 ID
children 可以包含
onentryonexittransitionstateparallelhistorydatamodelinvoke
XState 可以直接在 State Node 的 type: parallel 进行指定实现。
示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial网盘parallel id网盘state id上传initialtransition target空闲 //initialstate id空闲transition target上传中 event开始 //statestate id上传中transition target成功 event完在 //statestate id成功/state/statestate id下载initialtransition target下载.空闲 //initialstate id下载.空闲transition target下载.下载中 event开始 //statestate id下载.下载中transition target下载.成功 event完在 //statestate id下载.成功/state/state/parallel
/scxmlMachine({id: 状态机,initial: 网盘,states: {网盘: {type: parallel,states: {下载: {initial: 空闲,states: {空闲: {on: {开始: 下载中,},},下载中: {on: {完成: 成功,},},成功: {},},},上传: {initial: 空闲,states: {空闲: {on: {开始: 上传中,},},上传中: {on: {完成: 成功,},},成功: {},},},},},},
});3.1.6 final
final 表示 scxml 或复合 state 元素的最终状态。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxmlstate id下载中transition event完成 target成功 //statefinal id成功 /
/scxml属性字段描述如下
名称必填属性约束类型默认值有效值描述idfalsenoneIDnoneXML Schema 中定义的有效 id状态 ID
children 可以包含
onentryonexitdonedata
XState 可以直接在 State Node 的 type: final 进行指定实现。
示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxmlinitialtransition target工作 //initialstate id工作initialtransition target正在完成任务 //initial!-- 子状态为 final 时父状态触发 don.state 事件 --transition eventdone.state.工作 target工作完成 /state id正在完成任务transition event完成 target任务完成 //statefinal id任务完成/final/statefinal id工作完成 /
/scxmlMachine({id: 状态机,initial: 工作,states: {工作: {initial: 正在完成任务,states: {正在完成任务: {on: {完成: 任务完成,},},任务完成: {type: final,},},onDone: 工作完成,},工作完成: {},},
});3.1.7 history
history 伪状态允许状态机记住它的状态配置。以 history 状态为目标的 transition 会将状态机返回到此记录的配置。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxmlhistory id历史状态 typeshallowtransition target状态A //historystate id状态A/state
/scxml属性字段描述如下
名称必填属性约束类型默认值有效值描述idfalsenoneIDnoneXML Schema 中定义的有效 id状态 IDtypefalsenoneenum“shallow”“deep” 或 “shallow”确定是记录当前状态的活动原子子状态还是仅记录其直接活动子状态。
children 可以包含 transition。
transition ‘target’ 指定默认历史配置的转换。 仅发生一次。 在符合标准的 SCXML 文档中此转换不得包含“cond”或“事件”属性并且必须指定一个非空“target”。此转换可能包含可执行内容。 如果 ‘type’ 是“shallow”那么这个 transition 的 ‘target’ 必须只包含父状态的直接子级。 否则它必须只包含父级的后代。
XState 可以直接在 State Node 的 type: history 进行指定实现。多了一些额外属性
{type: history,history: shallow,target: 默认指定到父状态
}示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial新建state id新建initialtransition target编写中/transition/initialtransition target中断 event暂停/transitionstate id编写中transition target预览中 event下一步/transition/statestate id预览中transition target提交中 event下一步/transition/statestate id提交中/statehistory id历史状态 typeshallow/history/statestate id中断transition target历史状态 event恢复/transition/state/scxmlMachine({id: 状态机,initial: 新建,states: {新建: {initial: 编写中,on: {暂停: 中断,},states: {编写中: {on: {下一步: 预览中,},},预览中: {on: {下一步: 提交中,},},提交中: {},历史状态: {type: history,},},},中断: {on: {恢复: 新建.历史状态,},},},
});3.1.8 onentry
onentry一个包装元素包含进入状态时要执行的可执行内容。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Astate id状态Aonentrylog expr欢迎进入状态A //onentry/state
/scxml同 transitionchildren 只能包含 可执行内容。
XState 可以直接在 State Node 的 onEntry 进行定义。
示例
Machine({id: 状态机,initial: 状态A,states: {状态A: {onEntry: actions.log(欢迎进入状态A),},},
});3.1.9 onexit
onexit一个包装元素包含退出状态时要执行的可执行内容。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Astate id状态Aonexitlog expr欢迎下次再来状态A //onexit/state
/scxml同 children 只能包含 可执行内容。
XState 可以直接在 State Node 的 onExit 进行定义。
示例
Machine({id: 状态机,initial: 状态A,states: {状态A: {onExit: actions.log(欢迎下次再来状态A),},},
});3.2 可执行内容
可执行内容只能在 onentry、onexit 和 transition 中使用。它提供了允许 SCXML 会话修改其数据模型并与外部实体交互的钩子。
不仅包括了 raise、foreach、log、if、elseif、else还包含了其他分组下的 script 、assign、send 、cancel。当然 下面我们还是按照规范文档中的分类进行对应说明。
在 XState 中所有的在 SCXML 中的“可执行内容”统称为 action。所以对应的这些“可执行内容”都在 XState 的 actions 包中。
3.2.1 raise
raise 元素在当前 SCXML 会话中引发一个事件。可以触发 transition 中的 event。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Astate id状态Aonentryraise event跳转 //onentry/state
/scxml属性字段描述如下
名称必填属性约束类型默认值有效值描述eventtrueNMTOKENnone指定事件的名称。这将与转换的“event”属性相匹配。
对应 XState 的 actions.raise 函数。示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Astate id状态Atransition target状态B event跳转/transitiononentryraise event跳转 //onentry/statestate id状态B/state
/scxmlMachine({id: 状态机,initial: 状态A,states: {状态A: {onEntry: actions.raise(跳转),on: {跳转: 状态B,},},状态B: {},},
});3.2.2 foreach
foreach 元素允许 SCXML 应用程序遍历 datamodel 中的集合并为集合中的每个项目执行其中包含的 可执行内容。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Adatamodeldata expr[ 10, 20, 30 ] iddataArr //datamodelstate id状态Aonentryforeach arraydataArr indexvarIndex itemvarItemlog exprvarIndex /log exprvarItem //foreach/onentry/state
/scxml属性字段描述如下
名称必填属性约束类型默认值有效值描述arraytrueValue expressionnone计算结果为可迭代集合的值表达式 foreach元素将遍历此集合的浅表副本itemtruexsd:stringnone在指定数 datamodel 中有效的任何变量名在循环的每次迭代中存储集合的不同项的变量indexfalsexsd:stringnone在指定数 datamodel 中有效的任何变量名在 foreach 循环的每次迭代中存储当前迭代索引的变量
children 由一个或多个 可执行内容组成。
可以对应 XState 的 actions.pure 函数它也可以返回一个或者一组 action或者什么也不返回。当然这个函数更灵活。示例
Machine({id: 状态机,initial: 状态A,context: {dataArr: [10, 20, 30],},states: {状态A: {onEntry: actions.pure((context, event) {const _actions [];context.dataArr.map((varItem, varIndex) {_actions.push(actions.log(varIndex.toString()));_actions.push(actions.log(varItem.toString()));});return _actions;}),},},
});3.2.3 log
log 允许应用程序生成日志记录或调试消息。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Astate id状态Aonentrylog expr欢迎进入状态A //onentry/state
/scxml属性字段描述如下
名称必填属性约束类型默认值有效值描述labelfalsestring空字符串具有依赖于实现的解释的字符串。它旨在提供有关“expr”指定的日志字符串的元数据。exprfalse值表达式none返回要记录的值的表达式
对应 XState 的 actions.log。示例
Machine({id: 状态机,initial: 状态A,states: {状态A: {onEntry: actions.log(欢迎进入状态A),},},
});3.2.4 if、elseif、else
if 是条件执行元素的容器。 elseif 是一个空元素它对 if 的内容进行分区并提供一个判断是否执行分区的条件。 else 是一个空元素用于划分 if 的内容。它等价于一个带有“cond”的 elseif它总是计算为真。
if condcond1log exprcond1true /elseif condcond2 /log exprcond2true /elseif condcond3 /log exprcond3true /else /log expr其他情况 /
/if属性字段描述如下
名称必填属性约束类型默认值有效值描述condtrue条件表达式none有效的条件表达式一个布尔表达式
XState 中有很多方法可以实现类似的能力如果非要对标的话就是 actions.choose 函数了。示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Adatamodeldata expr2 idvalue //datamodelstate id状态Aonentryif condvalue 1log exprvalue 1 /elseif condvalue 2 /log exprvalue 2 /else /log exprvalue ! 1 value ! 2 //if/onentry/state
/scxmlMachine({id: 状态机,initial: 状态A,context: {value: 2,},states: {状态A: {onEntry: actions.choose([{cond: (context, event) context.value 1,actions: [actions.log(value 1)],},{cond: equal2,actions: [actions.log(value 2)],},{actions: [actions.log(value ! 1 value ! 2)],},]),},},},{guards: {equal2: (context) context.value 2,},}
);3.3 数据模型和数据操作
这部分是状态之外的数据部分的定义和操作。
数据模型通过 datamodel 元素定义该元素包含零个或多个 data 元素每个元素定义一个数据元素并为其分配一个初始值。 这些值可以在线指定或从外部源加载。 然后可以通过 assign 元素更新它们。 donedata、content 和 param 元素可用于将数据合并到与外部实体的通信中。 最后script 元素允许结合脚本。
3.3.1 datamodel
datamodel 是一个包装器元素它封装了任意数量的 data 元素每个元素都定义了一个数据对象。
children 只能包含 data。
对应 XState 的顶层 context。
3.3.2 data
data 元素用于声明和填充数据模型的部分。
datamodeldata exprtrue idVarBool /data expr1 idVarInt /data expr这是字符串 idVarString /
/datamodel属性字段描述如下
名称必填属性约束类型默认值有效值描述idtrueIDnone数据项的名称srcfalseURInone给出应从中获取数据对象的位置exprfalseExpressionnone有效的条件表达式执行以提供数据项的值
在符合标准的 SCXML 文档中data 元素可以具有“src”或“expr”属性但不能 同时具有。此外如果任一属性存在data 元素 绝不能有 children。因此‘src’、‘expr’ 和 children 在 data 元素中是互斥的。
在 XState 中直接作为 context 字段的值存在。
示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Adatamodeldata exprtrue idVarBool /data expr1 idVarInt /data expr这是字符串 idVarString //datamodelstate id状态Aonentrylog exprVarBool /log exprVarInt /log exprVarString //onentry/state
/scxmlMachine({id: 状态机,initial: 状态A,context: {varBool: true,varInt: 1,varString: 这是字符串,},states: {状态A: {onEntry: [actions.log((context) context.varBool),actions.log((context) context.varInt),actions.log((context) context.varString),],},},
});3.3.3 assign
assign 元素用于修改数据模型。
assign locationVar1 expr5/属性字段描述如下
名称必填属性约束类型默认值有效值描述locationtrue路径表达式none有效的路径表达式数据模型中要插入新值的位置exprfalse此属性不得出现在具有子元素的 assign 元素中值表达none有效的值表达式返回要分配的值的表达式
在 XState 中用 actions.assign 函数表示。
示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Adatamodeldata expr1 idVarInt /data expr这是字符串 idVarString //datamodelstate id状态Aonentryassign expr5 locationVarInt /assign expr新的字符串 locationVarString //onentry/state
/scxmlMachine({id: 状态机,initial: 状态A,context: {varInt: 1,varString: 这是字符串,},states: {状态A: {onEntry: actions.assign({varInt: 5,varString: 新的字符串,}),},},
});3.3.4 script
script 元素将脚本功能添加到状态机。
scriptconsole.log(Hello, world!)/script属性字段描述如下
名称必填属性约束类型默认值有效值描述srcfalse如果元素有子元素则可能不会发生none有效的 URI给出应该下载脚本的位置
script 元素的 children 内容表示要执行的脚本代码。
XState 在很多地方可以表达类似的能力如 actions 属性是支持直接赋值函数的在 actions.log、actions.pure、actions.assign 等函数都可以实现类似能力。
3.3.5 donedata
一个包装元素保存进入 final 状态时要返回的数据。
final id最终状态donedataparam exprvalue1 namekey1 /param exprvalue2 namekey2 //donedata
/finalchildren 可以包含
content可以出现 0 次或 1次。param可以出现 0 次或多次。
一个符合标准的 SCXML 文档必须指定单个 content 元素或一个或多个 param 元素作为 donedata 的子元素但不能同时指定两者。 如果 SCXML 处理器在进入最终状态时生成“done”事件它必须执行 donedata 元素 param 或 content 子元素并将结果数据放在 _event.data 字段中。
对应到 XState State Node 的 data 属性字段。
示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Astate id状态A initial状态A1transition target状态B eventdone.state.状态Alog expr_event.data/log/transitionfinal id状态A1donedataparam expr1 namefinalCustomeData1 /param expr2 namefinalCustomeData2 //donedata/final/statestate id状态B/state
/scxmlMachine({id: 状态机,initial: 状态A,context: {},states: {状态A: {initial: 状态A1,onDone: {actions: actions.log((context, event) {return event.data;}),target: 状态B,},states: {状态A1: {type: final,data: {finalCustomeData1: 1,finalCustomeData2: 2,},},},},状态B: {},},
});3.3.6 param
param 标签提供了一种识别键和动态计算值的通用方法该值可以传递给外部服务或包含在事件中。
final id最终状态donedataparam exprvalue1 namekey1 /param exprvalue2 namekey2 //donedata
/final属性字段描述如下
名称必填属性约束类型默认值有效值描述nametrueNMTOKENnone字符串exprfalse值表达式none有效值表达式locationfalse位置表达式none有效位置表达式
类似于一个 key 一个 value 的方式定义事件数据。
XState 比较灵活直接在事件返回处填写 Object 即可。
3.3.7 content
包含要传递给外部服务的数据的容器元素。
final id最终状态donedatacontent{key1: value1, key2: value2}/content/donedata
/final属性字段描述如下
名称必填属性约束类型默认值有效值描述exprfalse不得与子内容一起出现值表达式none有效值表达式
如果“expr”属性不存在处理器必须使用 content 的子元素作为输出。
类似于一个 Object 的方式定义事件数据。功能和 param 相似。可以在 donedata、send、invoke 中使用。
XState 比较灵活直接在事件返回处填写 Object 即可。
3.4 外部通讯
外部通信功能允许 SCXML 会话从外部实体发送和接收事件并调用外部服务。
3.4.1 send
send 用于将事件和数据发送到外部系统包括外部 SCXML 解释器或在当前 SCXML 会话中引发事件。提供“即发即弃”的能力。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Astate id状态Aonentrysend event跳转 //onentry/state
/scxml属性字段描述如下
名称必填属性约束类型默认值有效值描述eventfalse不得与“eventexpr”一起出现EventType.datatypenone一个字符串指示正在生成的消息的名称eventexprfalse不得与“event”一起出现值表达式none“event”的动态替代方案。如果此属性存在SCXML 处理器必须在执行父 send 元素时对其进行执行并将结果视为已作为“event”的值输入targetfalse不能与“targetexpr”一起出现URInone有效的目标 URI平台应将事件发送到的消息目标的唯一标识符targetexprfalse不能与“target”一起出现值表达式none有效目标 URI 的表达式“target”的动态替代方案。如果存在此属性则 SCXML 处理器必须在执行父 send 元素时对其进行执行并将结果视为已作为“target”的值输入typefalse不能与“typeexpr”一起出现URInone标识消息传输机制的 URItypeexprfalse不能与“type”一起出现值表达式none“type”的动态替代方案。如果此属性存在SCXML 处理器必须在执行父 send 元素时对其进行执行并将结果视为已作为“type”值输入idfalse不得与“idlocation”一起出现xml:IDnone要用作此 send 实例的标识符的字符串文字idlocationfalse不得与“id”一起出现位置表达式none任何位置表达式执行为可以存储系统生成的 id 的数据模型位置delayfalse不能与“delayexpr”或属性“target”具有值“_internal”一起出现Duration.datatypenone指示处理器在分派消息之前应等待多长时间delayexprfalse不得出现在“delay”或属性“target”的值为“_internal”时值表达式none“delay”的动态替代方案。如果此属性存在SCXML 处理器必须在执行父 send 元素时对其进行执行并将结果视为已作为“delay”值输入namelistfalse不得与 content 元素一起指定位置表达式列表none一个或多个数据模型位置的空格分隔列表作为属性/值对包含在消息中。 位置的名称是属性存储在位置的值是值。
children 可以包含
content可以出现 0 次或 1次。param可以出现 0 次或多次。
符合标准的 SCXML 文档必须准确指定“event”、“eventexpr”和 content 之一。符合标准的文档不得在 content 中指定“namelist”或 param。
SCXML 处理器必须包含 param 或 ‘namelist’ 提供的所有属性和值即使出现重复也是如此。如果存在“idlocation”SCXML 处理器必须在执行父 send 元素时生成一个 id 并将其存储在此位置。如果通过“delay”或“delayexpr”指定延迟SCXML 处理器必须将字符串解释为时间间隔。 它必须仅在延迟间隔过去后才发送消息。 请注意发送标记的执行将立即返回。处理器必须在执行 send 元素时执行所有参数到 send而不是在实际发送消息时执行。 如果 参数的执行产生错误处理器必须丢弃该消息而不尝试传递它。 如果 SCXML 会话在延迟间隔过去之前终止则 SCXML 处理器必须丢弃该消息而不尝试传递它。
对应 XState 的 actions.send 函数。结构类似
{event: scxml.event,options: {id: scxml.id,delay: scxml.delay,to: scxml.target}
}示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Astate id状态Atransition target状态B event跳转/transitiononentrysend event跳转 //onentry/statestate id状态B/state
/scxmlMachine({id: 状态机,initial: 状态A,states: {状态A: {onEntry: actions.send(跳转),on: {跳转: 状态B,},},状态B: {},},
});3.4.2 cancel
cancel 元素用于取消延迟的 send 事件。 SCXML 处理器不得允许 cancel 影响未在同一会话中引发的事件。 处理器应尽最大努力取消具有指定 ID 的所有延迟事件。 但是请注意它不能保证成功例如如果事件在 cancel 标记执行时已经交付。
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial状态Astate id状态Aonentrycancel sendid跳转ID //onentry/state
/scxml属性字段描述如下
名称必填属性约束类型默认值有效值描述sendidfalse不得与 sendideexpr 一起出现IDREFnone延迟事件的 sendid要取消的事件的 ID。如果多个延迟事件有这个 sendid处理器将全部取消sendidexprfalse不得与 sendid 一起出现值表达式none计算结果为延迟事件 ID 的任何表达式‘sendid’ 的动态替代方案。如果此属性存在SCXML 处理器必须在执行父 cancel 元素时对其进行执行并将结果视为已作为“sendid”的值输入
对应 XState 的 actions.cancel 函数。
示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial未登录state id未登录transition target已登录 event登录/transition/statestate id已登录transition target已登录 event活动/transitiononentrysend event注销 delay1000 * 60 target未登录 id消息ID //onentryonexitcancel sendid消息ID/cancel/onexit/state
/scxmlMachine({id: 状态机,initial: 未登录,states: {未登录: {on: {登录: 已登录,},},已登录: {onEntry: actions.send(注销, {delay: 1000 * 60,id: 消息ID,}),onExit: actions.cancel(消息ID),on: {注销: 未登录,活动: 已登录,},},},
});也可以使用 XState 的 after 语法糖来实现
Machine({id: 状态机,initial: 未登录,states: {未登录: {on: {登录: 已登录,},},已登录: {after: {[1000 * 60]: 未登录,},on: {活动: 已登录,},},},
});3.4.3 invoke
invoke 元素用于创建外部服务的实例。
invoke idID_SUB srcsub.scxmlparam expr3 namei_ID /
/invokeinvoke 提供了一种更紧密耦合的通信形式特别是能够触发平台定义的服务并将数据传递给它。 它及其子 finalize 在模拟外部服务行为的状态中很有用。 invoke 元素在状态的 onentry 元素之后执行并导致创建外部服务的实例。 param 和 content 元素以及 ‘namelist’ 属性可用于将数据传递给服务。 当并行状态同时调用相同的外部服务时将启动外部服务的单独实例。 它们可以通过与它们相关联的 id 来区分。 类似地从外部服务返回的事件中包含的 id 可用于确定哪些事件是对哪些调用的响应。 返回的每个事件将仅由调用它的状态中的 finalize 处理但该事件随后会像状态机接收的任何其他事件一样被处理。 因此finalize 代码可以被认为是在将事件添加到事件队列之前应用的预处理阶段。 请注意该事件将传递给所有并行状态以检查转换。 由于当状态机离开调用状态时调用将被取消因此在将立即退出的状态下开始调用是没有意义的。 因此invoke 元素在进入状态时执行但仅在检查无事件转换和未决内部事件驱动的转换之后。 如果找到任何此类启用的转换则立即执行该转换并立即退出该状态而不会触发调用。 因此只有在状态机达到稳定配置时才会触发调用即在等待外部事件时它将停留在其中的配置。
属性字段描述如下
名称必填属性约束类型默认值有效值描述typefalse不能与“typeexpr”属性一起出现URInone指定外部服务类型的 URItypeexprfalse不得与“type”属性一起出现值表达式none计算结果为 URI 的任何值表达式该 URI 将是 ‘type’ 的有效值“type”的动态替代方案。如果此属性存在SCXML 处理器必须在执行父 invoke 元素时对其进行执行并将结果视为已作为“type”的值输入srcfalseURInone要传递给外部服务的 URIsrcexprfalse值表达式none‘src’ 的动态替代方案。如果此属性存在SCXML 处理器必须在执行父 invoke 元素时对其进行执行并将结果视为已作为“src”的值输入idfalseIDnone要用作此 invoke 实例的标识符的字符串文字idlocationfalse位置表达式none任何对数据模型位置求值的数据模型表达式namelistfalse位置表达式列表none要作为属性/值对传递给调用服务的一个或多个数据模型位置的空格分隔列表。 位置的名称是属性位置存储的值是值。autoforwardfalse布尔值false指示是否将事件转发到调用的进程的标志
children 可以包含
content可以出现 0 次或 1次。param可以出现 0 次或多次。finalize可以出现 0 次或 1次。
当 autoforward 属性设置为 true 时SCXML 处理器必须将它接收到的每个外部事件的精确副本发送到调用的进程。SCXML 处理器必须在将事件从调用会话的外部事件队列中删除以进行处理时转发该事件。
外部服务在处理时可能会返回多个事件。如果 invoke 实例中有一个 finalize 处理程序创建了生成事件的服务则 SCXML 处理器必须在从事件队列中删除事件以进行处理之前立即执行该 finalize 处理程序中的代码。它绝不能在 invoke 的任何其他实例中执行 finalize 处理程序。一旦外部服务完成处理它必须返回一个特殊事件 done.invoke.id 到调用进程的外部事件队列其中id是对应 invoke 元素的调用ID。外部服务不得 在此完成事件之后生成任何其他事件。
invoke 的实现
包括父进程和子进程之间的通信是特定于平台的但是在被调用的进程本身是 SCXML 会话的情况下以下要求成立
如果 invoke 中的 param 元素的 name 与调用会话的顶级数据声明中的 data 元素的 id 匹配则 SCXML 处理器必须使用 param 元素作为相应 data 元素的初始值。顶级数据声明是包含在 scxml 子元素的 datamodel 元素中的那些声明。请注意这意味着在 data 元素中指定的任何值都将被忽略。 namelist 类似。如果名称列表中键的值与调用会话的顶级数据模型中的 data 元素的 id 匹配 scxml 处理器必须使用键的值作为相应 data 元素的初始值。如果名称不匹配处理器不得 将 param 元素或名称列表键/值对的值添加到调用会话的数据模型中。但是处理器可以通过其他一些特定于平台的方式使这些值可用。当被调用的状态机达到顶级最终状态时处理器必须放置事件 done.invoke.id 上调用机其中所述外部事件队列 ID 是用于此调用的调用ID。请注意达到顶级最终状态对应于机器的正常终止并且一旦处于此状态它就无法生成或处理任何进一步的事件。如上所述如果调用状态机在接收到 done.invoke 之前退出包含调用的状态。id事件它取消调用的会话。执行此操作的方法是特定于平台的。然而当它被取消时被调用的会话必须在下一个微步结束时退出。处理器必须为被调用会话中的所有活动状态执行 处理程序但它不能 生成 done.invoke.id 事件。一旦取消调用的会话处理器必须忽略它从该会话接收到的任何事件。特别是它绝不能将它们插入到调用会话的外部事件队列中。SCXML 处理器必须支持使用 SCXML 事件I/O 处理器在调用会话和被调用会话之间进行通信。处理器可以支持使用其他事件I/O 处理器在调用会话和被调用会话之间进行通信。
对应 XState State Node 的 invoke 属性。描述如下
{id: ,src: ,autoForward: false,data: {},onDone: {},onError: {}
}示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial等待中state id等待中transition eventdone.invoke.子状态机 target时间到 /invoke id子状态机 typehttp://www.w3.org/TR/scxml/contentscxml name分钟子状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial等待中 initial激活中state id激活中onentrysend delay60s event结束/send/onentrytransition target完成 event结束/transition/statefinal id完成/final/scxml/content/invoke/statefinal id时间到/final
/scxmlconst minuteMachine Machine({id: 分钟子状态机,initial: 激活中,states: {激活中: {after: {60000: { target: 完成 },},},完成: { type: final },},
});Machine({id: 状态机,initial: 等待中,states: {等待中: {invoke: {src: minuteMachine,onDone: 时间到,},},时间到: {type: final,},},
});3.4.4 finalize
finalize 元素使调用会话能够使用被调用会话返回的事件中包含的数据更新其数据模型。
finalize 包含在执行 invoke 后外部服务返回事件时执行的可执行内容。 在系统查找与事件匹配的转换之前应用此内容。 在可执行内容中系统变量“_event”可用于引用正在处理的事件中包含的数据。在并行状态的情况下仅执行原始调用状态下的finalize代码。 在调用期间状态机从被调用组件接收到的任何事件都由 finalize 处理程序在选择转换之前进行预处理。 finalize 代码用于规范化返回数据的形式并在执行转换的“event”和“cond”子句之前更新数据模型。 在符合的SCXML文件在敲定的可执行内容不得引发事件或调用外部动作。特别是send 和 raise 元素 不得出现。
children 可以包含 可执行内容。
XState 没有对应的 APIXState 对于处理消息是非常灵活的所以这一块能力是内置进去的。
示例
scxml name状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial等待中datamodeldata expr1 idVarValue //datamodelstate id等待中transition eventchildToParent condVarValue2 target结束 /invoke id子状态机 typehttp://www.w3.org/TR/scxml/contentscxml name发送消息到父级子状态机 version1.0 xmlnshttp://www.w3.org/2005/07/scxml initial完成 initial激活中final id完成onentrysend target#_parent eventchildToParentparam nameaParam expr2 //send/onentry/final/scxml/contentfinalizeassign locationVarValue expr_event.data.aParam//finalize/invoke/statefinal id结束/final
/scxml3.5 未对应 XState API
上面按照规范与 XState 进行了对应。还有一部分是 XState 特色产物。如下
ActorActor 模型一套非常成熟的模型。用来扩展子状态机。Interpreter由于 XState 的状态机是一套纯函数编写无任何副作用。所以官方提供了一个 Interpreter 用来托管副作用。Model用来改善开发人员体验分离和组织 context 和 event共享模型。
3.6 对应大图
整个对应关系大致如下图所示
4. 最后
连续熬了一个多星期的夜对 SCXML 和 XState 的关系进行了梳理和对齐最终产出了这篇 4万 多字的文章。
做为一个 XState 的 “过来人”这篇从规范到工具对应关系的文章正是当初那个在入门产生疑惑时的我最需要的东西。
也希望这篇文章可以帮助入门和使用状态机及 XState 的同学解除部分疑惑。
⭐ Github 文章地址