Start and complete background execution

In the simple case described in Create a callback, the background execution is unaware that it is running in the background and does not perform any additional processing.

If the background execution does itself want to invoke further asynchronous processing, then it is important to integrate this with that of its caller. For example:

  • Action A calls action B asynchronously
  • Action B calls action C asynchronously
  • Action A should only be considered complete when action C completes

Signalling start and complete

The example below shows what the process for action C might look like.

[
  {
    "name": "start",
"comment": "Signal to Action B that Action C has started.",
  "script": "stepTypeCallback",
    "config": {
    "callbackAction": "start"
    },
    "next": {
    "start": "run",
      "*": false
    }
  },
{
"name": "run",
// Do work in C
"next": "complete"
},
// Do work in C
  {
    "name": "complete",
"comment": "Signal to Action B that Action C is complete.",
   "script": "stepTypeCallback",
    "config": {
    "callbackAction": "complete"
    },
    "next": false
  }
]

The start step uses the "start" callback action to send a start status. By default, it uses the callback token found in %{param.data:callbackToken} — the value provided in the call step's callbackToken field.

After doing work, C signals completion using the complete step. The "complete" action, like all other actions except "start" and "anchor", defaults the callback token to "%{attr:callbackToken}", i.e. the token output from the start step.

Nested background processes

Each background execution holds a callback token, which is used to report progress or completion to its anchor step. If the execution was created by another background execution, it may also receive a parent token. In this case, the child execution may need to send its final callback to the parent execution's anchor step, rather than its own.

To illustrate, action B is called in the background from action A, and also calls action C. This is a bit more complicated.

[
  {
    "name": "start",
"comment": "Signal to Action A that Action B has started.",
  "script": "stepTypeCallback",
    "config": {
    "callbackAction": "start"
    },
    "next": {
    "start": "anchor",
      "*": false
    }
  },
{
  "name": "anchor",
  "script": "stepTypeCallback",
    "config": {
    "parentToken": true
    },
    "next": {
    "create": "call",
    "complete": "completeB",
      "*": false
    }
  },
  {
    "name": "call",
  "script": "stepTypeCall",
    "config": {
    "callAction": "actionC",
    "background": true,
      "callback": true
    },
    "next": false
},
{
  "name": "completeB",
"comment": "Signal to Action A that Action B is complete.",
   "script": "stepTypeCallback",
    "config": {
    "callbackAction": "complete",
"callbackToken":"%{attr:parentCallbackToken}"
    },
    "next": false
  }
]

This process starts the same way as action C.

The anchor step references a parent token. This uses a value of true, to mean "%{attr:callbackToken}", i.e. the value set by the start step.

When Action B registered a callback to track the completion of Action C, it created a new child callback token (via an anchor step in B). That token was passed to Action C as part of the call.

When Action C finishes, it performs a complete action using that token. The resulting callback is routed to the anchor step in Action B — the one that originally registered it. At this point, the callbackToken attribute in B refers to the token B created for tracking C.

If Action B now needs to report its own completion to Action A, it cannot use the callbackToken in play. Instead, it must use the parent callback token — the one created by Action A when it originally called B. This token is automatically made available in the parentCallbackToken attribute during the callback.

Start conditions

As well as retrieving the parent token, the start step will check the status of the parent. If the parent is not active, the start step will end with a suitable state, and the action would not continue. This allows a calling processing to control executions.

Managing tokens

You can manage tokens in the callback step using configuration options.

Most of the time, the token handling defaults to what you want. The rules for resolving callback tokens are

  • For anchor action, when creating a callback:
    • The token option is ignored
    • parentToken can optionally be set.
    • If parentToken: true, uses %{attr:callbackToken}.
    • Writes to the new token to the callbackToken attribute.
  • For start action:
    • Defaults token to %{param.data:callbackToken} (i.e. token from the incoming call)
    • Writes token to callbackToken attribute.
  • For all other actions (progress, complete, etc.):
    • Defaults token to %{attr:callbackToken}
    • Writes token to callbackToken attribute.
  • When called from the child execution as part of a callback
    • Always sets token to %{param.data:callbackToken} (i.e. token from the incoming call)
    • Writes token to callbackToken attribute.
    • Writes parent token to parentCallbackToken attribute.

You can change the attribute to which the callback token is written by setting the "tokenAttribute" on the callback step. Similarly you can change the attribute to which the parent callback token is written by setting the "parentTokenAttribute" on the callback step.

In the call step you can set the callback token to use by setting the "callback" option. A value of true is the same as "%{attr:callbackToken}". This is passed as the "callbackToken" of the object in the <data> parameter.

In more complicated cases:

  • Use the "tokenAttribute" config option in the "start" step to change the attribute to which the parent token is written, typically to "callbackParentToken".
  • Reference that new attribute in the parentToken option in the "create" step, typically using the runtime placeholder "%{attr:callbackParentToken}".