Control flow
Flows are inherently asynchronous. Every step in a flow is executed asynchronously, however, the graphical representation abstracts the asynchronous nature, eliminating the callback hell.
The intuitive graphical representation of flows also makes it easy to build complex, asynchronous programs that is otherwise difficult to create and visualize in traditional programming. If the steps are wired in parallel, they will execute concurrently. If the steps are wired in a sequence, they will execute sequentially. To join two or more concurrent paths, simply join them together. All the joined steps will wait for each other to complete the execution before executing further. It's that simple!
The program flow can also be controlled by a transition's configuration, that will determine if the transition should happen or not during the runtime, depending on the current state. Different types of transitions such as normal, conditional, error, and always can decide the direction of program flow dynamically.
Steps in sequence
To execute steps in a sequential fashion, place them in a sequence. They will be executed one after the other. This is illustrated by the debug animation below:
Debugging a flow with steps wired in sequence. step2
will evaluate only after step1
completes the execution.
Steps in parallel
To execute steps in parallel, place them in parallel paths. Parallel steps are executed concurrently and do not wait for each other. Since they do not have connections between them, their output data cannot be accessed within each other. It makes sense as you cannot predict which step will complete first.
Debugging a flow with steps wired in parallel. step2
will evaluate simultaneously with step1
.
Joining parallel paths
Joining parallel paths is really easy and intuitive in Codeflow. To join two or more paths, simply have transitions that converge on another step. That step will wait until all the joined paths completes their execution. This lets you build asynchronous business logic without using tedious programming constructs or libraries.
As you can notice in the below animation of debugger in action, the steps step1
and step2
are invoked concurrently, and step3
is invoked only after both of them completes.
Animation of debugger in action. As you can notice, step1
and step2
starts the execution simultaneously. However, step3
waits until step2
and step1
complete the execution.
Conditional transition
As you have seen in the previous chapter, transitions can have conditions that determine the program flow during runtime. The below image shows an example of a conditional transition. The condition from start
to step2
is hard coded with boolean false. This prevents that transition from triggering at the runtime. Since step3
is connected to step2
and no other steps, the transition from step2
to step3
also will not occur. The condition expression can be any Codeflow expression and can include data from the previous steps to decide the program flow based on the current state.
Above image is showing the transition from start to step2 with a condition set to false.
The below debug animation shows the above flow in action. As you can see, the execution does not reach step2
and step3
. Also the transitions step2
-step3
and step3
-step4
are greyed out to convey that they do not occur.
Animation showing debugger in action. Transitions that are taken are highlighted in green, and the stale transitions are colored in grey.
Stale paths and joins
If a path is not taken due to a false condition, then the entire path consisting of steps and transitions that are linked only to that transition will also not be invoked. This will affect the control flow. Such a path is marked as stale and any joins that are waiting downstream won't wait for that path anymore.
Otherwise transition
There can be any number of conditional transitions from a step. The otherwise transition can be used to handle an else case where the transition should happen if none of the conditions evaluate to true.
The below animation shows two conditional transitions to handle two cases and an otherwise condition to capture every other cases. The otherwise transition will be triggered whenever there are no other successful transition.
Animation showing usage of otherwise transition.
The above flow is roughly equivalent to the below pseudo logic:
function checkShapes(string shape) {
if (shape == 'square')
print "Square shape"
elseif (shape == 'circle')
print "Circle shape"
else
print "unrecognized shape"
}
Otherwise transitions can also be chained to perform nested conditions. The below image shows a usage of multiple conditional and otherwise transitions to perform a nested logic.
Image of a flow that uses multiple conditional and otherwise transitions to perform a nested check.
And its roughly equivalent pseudo code:
function checkAge(integer age) {
if (age <= 30) {
if (age < 18) {
print "Age < 18"
} else {
print "Age between 18 and 30"
}
} else {
print "Age greater than 30"
}
}
Exception handling
Exceptions can also be handled through transitions. Transition types 'error' and 'always' can be used to channel the control flow when an exception occurs.
Error transition
Transitions with type set as error will capture any error thrown by the source step. The exception data is captured in the step's output and can be used by subsequent steps and transitions to control the program flow.
The below animation shows an error handling in action during a debug session:
The designer shows exceptions in a different color for easy visual identification. There can be more than one error transition - and all of them are taken during exceptions.
Always transition
The always transition will happen no matter what. It is sort of roughly equivalent to the finally clause of few procedural programming platforms. There can be any number of always transitions, and all of them are traversed always, irrespective of whether other transitions occur or not.
Unhandled errors
Errors occuring in the steps that do not have an outgoing error or always transition becomes unhandled errors and are bubbled up the stack. If an unhandled error occurs in the middle of a sub flow, it will be thrown as an error of that step in the parent flow and will continue to bubble up the chain until handled.
Multiple returns
A flow can have more than one end
step. When there are two simultaneous end
steps, only one will return successfully. The first end
step to reach in the execution flow will succeed in setting the output value of the flow. All other end
's return values are ignored.
When there are both an unhandled or manually thrown error and a normal end
step, the return is taken on a first come first serve basis. Because of the asynchronous nature of execution, it is impossible to predict which step will return first on parallel paths. To prevent such ambiguous control flow, take extra care to design the flows that have parallel paths using error, always and conditional transitions and joins to ensure that the return happens in a predictable fashion.