Skip to main content

Async Method with Callback

Invoking service with callback allows the state to perform a complex operation and have multiple transitions based on the operation results. Apart from the next chapter (async service with onDone and onError target states) service here you can define many events.

The service is constructed by calling public State WithInvoke(InvokeServiceAsyncDelegate invokeAsync, Action cleanUpAction = null); method.

First argument is the method to run that has as an argument the callback delegate, that you need to call to trigger event. Optionally send error object in the event if error was thrown.

Second argument is a cleanup action that is called when state machine leaving this state, you can define there any cleanup code to release resources. unsubscribe from messages, cleanup some database, etc. The cleanup code is optional.

As an example let's build a simple vending "waiting for user" state.

waiting for user

// imagine we have a vending machine that awaits for
// user to come or an administrator to request a maintenance
State waitingForUser = new State("waitingForUser")
.WithInvoke(async (callback) => {
// imagine we have an MQTT message to arrive when user signs in to a vending machine
// we need to wait fot that
mqttClient.Subscribe("user/unlockedDoor");
// as well administrator can come and request maintenance mode
// we wait for it in parallel
mqttClient.Subscribe("adminUser/MaintenanceRequested");
mqttClient.OnMessage((topic, message) => {
switch(topic)
{
// based on the arrived message, we trigger event
case "user/unlockedDoor":
await callback("USER_UNLOCKED_DOOR");
break;
case "adminUser/MaintenanceRequested":
await callback("ADMINISTRATOR_MAINTENANCE_ACCESS_REQUESTED");
break;
}
});
}, () => {
// as we are leaving the state, to make sure those messages are
// not arriving then we are not waiting for them, unsubscribe on cleanup code
mqttClient.Unsubscribe("user/unlockedDoor");
mqttClient.Unsubscribe("adminUser/MaintenanceRequested");
})
.WithTransition("USER_UNLOCKED_DOOR", "chekingUserBalance")
.WithTransition("ADMINISTRATOR_MAINTENANCE_ACCESS_REQUESTED", "startingMaintenanceMode");
danger

If the callback(...) is never called, the service will never exit. Avoid such situations, each service must have an exit point.

Error cases

No transition was registered

No transition was registered .WithTransition("MY_EVENT", "nextStateId") but callback with this event was called callback("MY_EVENT") - this is not throwing an error as we think that service can be reused in different states. In some states event is useful, but another states may not care about it.

As an example: we have a service to notify user by email. In some state it can be mandatory, in another we may ignore if this service fails.

Transition is registered but target state is not found

You registered a transition .WithTransition("MY_EVENT", "nextStateId"), but you did not create a state with ID "nextStateId". This case will throw an exception "No target state found". If state has a transition, it needs to navigates to the existing state.