Tutorials Examples

Drakon.Tech JavaScript tutorial: use scenarios to handle long-running operations in a non-blocking way

Contents

Drakon.Tech scenarios are long-running tasks. Scenarios are an additional type of DRAKON charts in Drakon.Tech. The standard DRAKON charts are JavaScript functions that return quickly and do not block. A scenario, on the other hand, can execute over a long time. Scenarios perform web-service calls, wait for user input, and listen to events.

Scenarios display a continuous, unbroken control flow without callback hell and promise chaining.

Here is an example of a Drakon.Tech scenario. The source code of this demo project can be found here. The runnable demo is here.

An example Drakon.Tech scenario

The sm object

The code that Drakon.Tech generates from scenarios contains references to an sm global object. This object should have methods for working with state machines, such as sm.createMachine(), sm.addChild(), sm.sendMessage(), etc. You can either write your implementation of sm or use the smachine library hosted on Drakon.Tech.

In the latter case, add this line in the HTML before referencing your script

<script src="/gen/8ujt6myc2EBP9BfMWqydgYt6CrOGbdaD/smachine.js"></script>

Right-click on the module and choose "Properties." Then, click the "Edit HTML" button.

Initialize the sm global object with the result of the smachine() function somewhere at the top of the module. The module's header is a good place for this. Right-click on the module > "Properties" > "Header".

var sm = smachine()

How to make a scenario in Drakon.Tech

  1. Create a DRAKON chart inside a JavaScript module.
  2. Right-click on the header of the diagram and open "Properties."
  3. Change the type of diagram from "Function" to "Scenario."
Set type to Scenario

How to run a scenario from vanilla JavaScript

Follow these steps to run the scenario from a normal JavaScript function:

  1. Create the scenario object by calling the scenario function.
var scenario = runme()
  1. Start the scenario. The "run()" function will return immediately, and the scenario will run in the background.
scenario.run()

Store all data in the "self" object

The self variable in a scenario points to the scenario object. All data related to the scenario should be kept in the properties in the self object. Local variables in scenarios are not allowed.

This code will not work:

var x = doSomething()
useValue(x)

The correct version:

self.x = doSomething()
useValue(self.x)

If the scenario has arguments, they will also be stored as properties in the self object.

How to run a scenario from within a scenario

To call another scenario, use the "Insertion" icon. The "Insertion" icon creates a child scenario, starts it, and then waits for it to complete.

It is possible to return values from scenarios. In this example, sendHttpRequest is another scenario that sends a request to the server and returns the response.

Use Insertion to call other scenarios

Do not pass the self object in the arguments, as it will be passed automatically as the self.parent property.

How to wait for an event

To wait for an event, use the "Input" icon. Waiting for an event in a scenario means waiting for a specific method to get called. This event method could be called, for example, from a callback function bound to a click event.

Use Input to wait for an event

A scenario can wait for only one method at a time. When different events can occur, send the distinguishing information in the argument to the event function. Here, the button id is passed as the argument to tell the scenario which button was clicked:

sm.addEvent(postsButton, "click", function() {
    someScenario.onButtonClicked("posts")
})
sm.addEvent(commentsButton, "click", function() { 
    someScenario.onButtonClicked("comments")
})

How to pause a scenario

To temporary freeze a scenario from the inside, use the "Pause" icon. The "pause" icon should contain a JavaScript expression that evaluates to the number of milliseconds.

Pause

How to cancel a scenario

To cancel a scenario, use the sm.killMachine() function. sm.killMachine() will stop the scenario and all its children. Stopping a scenario will discard all pending IO-operations, timeouts and messages scheduled with sm.sendMessage() function. sm.killMachine() stops the whole subtree of scenarios.

sm.killMachine(machineObject)

How to handle errors

To add custom error handling, add a onError method to the self object. By default, an unhandled exception will propagate up the scenario tree until the exception hits a scenario that has onError. All the scenarios on the way will be canceled together with their children. There are several strategies for implementing a onError function:

  1. Do nothing. The scenario will stop and never return control to its parent. The parent scenario will thus freeze. It's an acceptable way to show a panic screen.

  2. Return a value to the parent (assuming self is available inside onError).

sm.sendMessage(self.parent, "onChildCompleted", someValue);
  1. Raise a new error:
sm.raiseError(self.parent, errorInfo)
  1. Continue to the next step.
self.run()

Scenarios vs. async/await

Scenarios are similar to async functions in JavaScript. However, programming with scenarios has advantages over async/await.

Conclusion

Drakon.Tech scenarios keep the control flow unbroken for algorithms that involve waiting. Like async functions, scenarios represent long-running procedures. However, scenarios are more expressive because they can wait for events. Drakon.Tech scenarios are also easier to debug and give the developer more control.