代码解读:
-
接口定义:
Node接口表示工作流中的一个节点,包含节点的 ID、可选参数和后继节点列表。Config接口表示整个工作流的配置,包含所有节点和起始节点的 ID。
-
配置解析:
- 使用
JSON.parse将配置字符串解析为Config对象。 - 配置中定义了节点的关系,描述了工作流的拓扑结构。
- 使用
-
WorkflowEngine 类:
-
属性:
nodeMap:节点映射,方便通过 ID 获取节点信息。predecessors:前驱节点映射,记录每个节点的前驱节点集合。completedNodes:已完成节点集合,记录哪些节点已经执行完成。executingNodes:正在执行节点集合,防止重复执行。executionPromise:用于等待所有节点执行完成的 Promise。resolveExecution:当所有节点执行完成时调用的函数,用于触发executionPromise。
-
构造函数:
- 调用
buildNodeMap()方法构建节点映射。 - 调用
buildPredecessors()方法构建前驱节点映射。 - 初始化
executionPromise,并获取resolveExecution函数。
- 调用
-
buildNodeMap() 方法:
- 遍历配置中的节点列表,将每个节点的 ID 和节点对象存入
nodeMap。
- 遍历配置中的节点列表,将每个节点的 ID 和节点对象存入
-
buildPredecessors() 方法:
- 初始化每个节点的前驱集合为空集合。
- 遍历每个节点的后继节点列表,将当前节点添加到后继节点的前驱集合中。
-
execute() 方法:
- 找出所有没有前驱节点的节点(即前驱集合为空),将其添加到
readyNodes集合中。 - 遍历
readyNodes,调用executeNode()方法异步执行这些节点。 - 使用
await等待executionPromise,当所有节点执行完成时,executionPromise会被resolve。
- 找出所有没有前驱节点的节点(即前驱集合为空),将其添加到
-
executeNode(nodeId: string) 方法:
- 检查节点是否正在执行或已完成,防止重复执行。
- 将节点添加到
executingNodes集合,表示正在执行。 - 模拟异步执行节点,使用
setTimeout模拟执行时间。- 对于
Node4,特意设置了 5 秒的延迟,模拟一个耗时较长的节点。
- 对于
- 节点执行完成后,将其从
executingNodes中移除,并添加到completedNodes集合。 - 检查所有节点是否已完成,如果是,则调用
resolveExecution(),触发executionPromise。 - 遍历节点的后继节点,更新后继节点的前驱集合,移除当前已完成的节点。
- 如果后继节点的前驱集合为空,说明其所有前驱节点都已完成,可以开始执行,递归调用
executeNode()。
-
-
执行流程:
- 实例化
WorkflowEngine,传入解析后的配置。 - 调用
execute()方法开始执行工作流。 - 使用
await等待execute()方法完成,即等待整个工作流执行结束。 - 执行过程中,节点按照依赖关系和可用性被调度执行。
- 节点可以并行执行,只要其前驱节点已完成。
- 整个流程执行完毕后,输出 "流程执行完成"。
- 实例化
示例运行结果:
正在执行节点 Node1
节点 Node1 执行完成
正在执行节点 Node2
节点 Node2 执行完成
正在执行节点 Node3
正在执行节点 Node4
正在执行节点 Node5
节点 Node3 执行完成
正在执行节点 Node6
节点 Node5 执行完成
节点 Node6 执行完成
节点 Node4 执行完成
正在执行节点 Node7
节点 Node7 执行完成
正在执行节点 Node8
正在执行节点 Node9
正在执行节点 Node10
节点 Node8 执行完成
节点 Node9 执行完成
节点 Node10 执行完成
正在执行节点 Node11
节点 Node11 执行完成
流程执行完成。
理解要点:
- 节点的并行执行: 只要节点的前驱节点都已完成,节点就可以开始执行,多个节点可以并行执行,充分利用系统资源。
- 节点的依赖关系: 通过前驱节点集合和后继节点列表,管理节点之间的依赖关系,确保执行顺序正确。
- 事件驱动模型: 在节点执行完成后,立即处理其后继节点,而不是等待整个批次完成,减少了不必要的等待时间。
- 流程的完成检测: 使用 Promise,在所有节点执行完成时触发,而不是通过轮询检查,提升了性能和代码的优雅度。