Tutorial 10. Concurrent programming with scenarios
How to make an async function
JavaScript's async/await construct is a clear and concise way to represent long-running operations.
To make a function async:
- Right-click on the function header and choose "Properties."
- Select the "Async function" function type.
Async functions can contain the await operator.
Scenarios are a more powerful alternative to async functions
In addition to async functions, Drakon.Tech offers scenarios to represent concurrent processes that run for extended periods. Scenarios are similar to async functions but are more advanced.
The critical difference with scenarios is that they can accept messages. Async functions cannot do that. There is no straightforward way to affect an async function after it has started. Scenarios, on the other hand, can pause themselves and wait for external input.
Another good thing about scenarios is that it is possible to shut them down and disregard any pending operations.
How to create a scenario
To turn a function into a scenario:
- Right-click on the function header and choose "Properties."
- Then, change the function type from "Function" to "Scenario." The word "scenario" will appear over the function header on the flowchart.
The "sm" object
The code that Drakon.Tech generates for scenarios assumes there is an sm object in the scope. The sm object must have methods like sm.createMachine(), sm.sendMessage(), sm.addChild(), sm.addMethod(), etc. You can implement the sm object yourself or use a module in the lib project.
How to use the existing sm module:
- Right-click on the module and choose "Properties."
- Click Dependencies.
- Add this line to the Dependencies section:
sm lib/sm_1_0
The End is not necessary
Scenario silhouettes do not need to have a branch with the End icon. There is a whole class of algorithms that do not have an end. These are control algorithms. Remove the branch with the End icon from a scenario silhouette if you are modeling an endless control algorithm.
How to start a scenario from a regular function
For each scenario, Drakon.Tech generates a scenario builder, a function that returns the scenario object. The scenario object has a "run" method that starts the scenario. The caller must start the scenario explicitly because a newly created scenario is not running.
The first argument of the scenario builder is the parent scenario. The parent can be undefined. If the parent is not undefined, the scenario will call the "onChildCompleted" method on the parent when the scenario finishes.
The rest of the arguments of the scenario builder come from the argument list of the scenario.
So the steps to start a scenario are:
- Call the scenario builder.
- Pass undefined in the first argument (unless you have a parent scenario).
- Pass the other arguments as specified in the argument list of the scenario.
- Call the "run" method on the object that the scenario builder returned.
The "self" object
A scenario is a plain JavaScript object. You can access this object with the self keyword inside the scenario flowchart.
It is possible to store data in the properties of the scenario object. There are, however, two properties that have a special meaning to the execution of the scenario: state and parent. Do not write any values to these two properties.
self.foo = "bar"
How to accept a message in a scenario
It is possible to pause a scenario until a specific method is called.
Add an Input icon to the scenario.
Type the signature of the method that the scenario will wait for. For example, sayHi().
Add arguments if needed. For example, sayHi(name).
This Input icon will have two effects.
- Drakon.Tech will add a method sayHi() to the scenario object.
- The Input icon will stop the scenario. When someone calls sayHi() at this point of the flow, the scenario will resume. If the sayHi method is called at any other position in the flow, the scenario will ignore it.
Let's invoke the sayHi method from the caller function. As you can see, the scenario continues and outputs the argument of the sayHi method to the console.
red black Hi, Mia
How to run an operation with a callback in a scenario
- Choose a name for the method that the callback will call
- Create a lambda that calls this method on the self object.
- Start the long-running operation and pass the lambda as a callback.
- Put an Input icon with the method name from the lambda.
The scenario will stop and wait for the operation to invoke the selected method from the callback.
How to wait for messages of several types
The Input icon tells a scenario: "stop and wait that someone calls this specific method."
If the scenario must accept one of several different messages, use a Choice icon with the keyword receive.
- Insert a Choice icon.
- Put the word receive in the Choice icon.
- In each of the Case icons, specify a method similar to the Input icon.
The Choice icon with the receive keyword tells the scenario: "Stop and wait that someone calls one of the methods specified in the case icons. Choose a path depending on which of the methods was called." In other words, the algorithm of the scenario shall take different paths for different methods.
Let's create a Choice-receive icon that would expect either a hello() or a bye() method.
We call the bye() method like this.
The console shows that the scenario continued and took the path from the correct Case icon.
Hi, Mia bye!
How to call a scenario from a scenario
To call one scenario from inside another, use the Insertion icon. The insertion icon to scenarios is what the await statement is to async functions. Here is how we use the Insertion icon.
- Add an Insertion icon.
- Call the scenario inside the Insertion icon.
- Pass the arguments specified in the called scenario. Do not pass the parent. The parent will be passed automatically. The parent will be available in the self.parent property.
- Do not call the run method.
Insertion icons are allowed only inside scenarios.
How to cancel a scenario
It is possible to discard a scenario that is waiting for external events. To do that, set the state property to undefined.
scenario.state = undefined
When someone invokes any method on a canceled scenario, the scenario will ignore such calls. If there is a pending operation that shall call a callback on a canceled scenario, such callback will be safely ignored.
How to pause a scenario
- Add a pause Icon.
- Specify the delay in the pause icon. The delay is a JavaScript expression that evaluates to a numeric value. This value is the number of milliseconds.
In this example, the scenario will pause for two seconds.
Scenarios are state machines
Scenarios are a way to represent Mealy finite-state machines.
- Each Input and Choice-receive icon becomes a state.
- The methods specified inside the Input and Choice-receive icons define what messages the machine accepts in each of the states.
- The way the flowchart connects the icons is the transition logic of the state machine.
Being state machines, scenarios are a good tool for modeling long-running operations, user interfaces, and control algorithms.