控制流程
Rivet中数据的流动(以及该流动的控制)是通过对节点图进行两次遍历来处理的。
第一遍:拓扑排序与入口点
第一次遍历节点是基于拓扑排序进行的。Rivet会找到所有没有依赖节点的节点。这些节点被视为图的"输出节点"。
Rivet 随后会找到所有依赖于输出节点的节点,依此类推,将该节点添加到"需要处理"列表中。
如果在此时遇到循环,Rivet 将正常继续执行。
在第一遍处理中,所有没有依赖项(没有数据流入)的节点将被标记为"输入节点"。
第二遍:执行
从第一轮标记的输入节点开始,rivet将以并行方式执行所有待处理节点。
每当当前正在执行的某个节点完成时,它将检查是否有任何依赖于它的节点已准备好执行。如果有,它将与其他当前正在执行的节点并行执行这些节点。
如果一个节点的所有依赖项都得到满足,则该节点被定义为准备执行。依赖项得到满足的条件是它所依赖的节点已完成执行并有值传递给依赖节点。
控制流排除项
当遇到If节点且该节点的输出不应运行时会发生什么?在这种情况下,If节点的输出是特殊的control-flow-excluded
值。如果将此值传递给任何节点,则该节点将不会执行。
然后,返回control-flow-excluded
的节点的每个依赖节点也将返回control-flow-excluded
,依此类推。在这方面,控制流排除会在值返回后"传播"到每个依赖节点。
许多节点可以返回一个control-flow-excluded
值,例如Match Node(对于不匹配的分支),以及Extract Object Path Node(当输入路径对给定对象无效时)。
控制流排除的消费者
某些类型的节点被注册为能够"消耗"control-flow-excluded
值。这意味着当节点遇到这个值时,它实际上会使用真正的control-flow-excluded
值运行。这使得某些节点能够"突破"control-flow-excluded
值的传播。
可以消耗control-flow-excluded
值的节点有:
- If/Else - 如果将
control-flow-excluded
传入If
端口,那么将转而传递Else
值。如果Else
值未连接,则结果将再次变为control-flow-excluded
。 - Coalesce -
control-flow-excluded
出于Coalesce节点的考虑将被视为"假值"。这些值将被跳过,而连接到Coalesce节点的后续真值将被传递。 - Race Inputs - 如果传入Race Inputs节点的某个分支返回
control-flow-excluded
,则该分支将不会被纳入竞争考虑。其他分支仍可能执行并返回值,该值将通过Race Inputs的输出传递。 - Graph Output - 一个Graph Output的
control-flow-excluded
可能会传递出图形,成为Subgraph节点的输出之一。这样,Subgraph的部分输出可能不会运行,而其他部分可能会运行。 - Loop Controller - 循环控制器需要消耗
control-flow-excluded
值才能多次运行。此外,将control-flow-excluded
传递给continue
端口会被视为循环的一次"成功"迭代,并会导致循环再次运行。
循环控制器
然而,循环控制器比较特殊,尤其是它的Break
端口。Break
端口在循环执行完成之前,不会将control-flow-excluded
值传递给下一个节点。否则,循环控制器本身就无法在最终将值传递给下一个节点之前多次运行。
如果循环控制器的任何其他输入端口接收到control-flow-excluded
值,那么循环控制器将不再运行,并将control-flow-excluded
值传递给连接到Break
的节点。因此,重要的是在循环内部使用If/Else或Coalesce节点作为"空值检查",以确保除非您有意为之,否则循环控制器永远不会接收到control-flow-excluded
值。