In http://www.tridiondeveloper.com/intro-to-the-event-system I described the overall event system, listing many of the options available, but not describing them in detail. One of these options requires further examination: Asynchronous Events.
Asynchronous Events happen outside of the regular process of the code that is running. They may run now, or they may run later. You can think of Asynchronous Events as “Fire And Forget” rockets. Sometimes that’s exactly what you need, and sometimes that’s exactly the wrong thing. Let’s look at some scenarios for both.
But first a quick review of the types of the event system.
There are four event phases that we use a lot:
- Initiated – The data has been received by the browser, and has not been written to the server
- Processed – The data has been written to the server, but has not been committed. An exception will perform an abort, and the data won’t be saved.
- TransactionCommitted – The data has been committed.
- TransactionAborted – The transaction has been aborted, and the data has not been written.
These phases happen in sequence. Initiated happens first, and when it has finished the event is in a Processed state. Once that finishes, the event is in either the TransactionCommitted or TransactionAborted state.
The Processed state is regularly when data validation happens. If everything is acceptable then it writes the data and the event moves to TransactionCommitted. If, on the other hand, there is an error or an unacceptable state then an exception is thrown and the event moves to a TransactionAborted state.
Let’s look at the two types of events that we might run on saving a component.
- We want to validate the component that we are trying to save; and
- We want to log every save event. We aren’t concerned if the save is successful.
The commands for these are:
EventSystem.Subscribe<Component, SaveEventArgs>(RunDataValidation, EventPhases.Processed); EventSystem.SubscribeAsync<Component, SaveEventArgs>(LogDataHandling, EventPhases.Processed);
In the first example we are running the RunDataValidation function now, as a function of the Processed phase. If it succeeds then we will be in the TransactionCommitted phase, and if it fails (and throws an error) then we will be in the TransactionAborted phase.
We use this when we are validating or manipulating data to be used in a later process.
Example processes include data manipulation or data verification, where the results are used later.
We use a synchronous event handler when we want the results of that function to be used in or to control the next step in the process.
In the second example we are running the LogDataHandling function at some point in the future. The call has a copy of the data that will be passed to it, and the function will be run when the system has some capacity to run it.
With an Async event handler, the function may be before the next phase, or it may not. And this is where the problem often lies.
A common error is that an asynchronous handler is called, and it validates or manipulates data that is used later on in the process. By the time this handler completes its work, the caller may have moved on and the updated data is now meaningless.
Example processes include logging data or sending an e-mail notification, where the results of the process are not required.
We use an asynchronous event handler when the data used in the handler, or the success or failure of the handler, will have no impact on the next step, or on any future step, in the process.
Most of the time we want to subscribe to an event using EventHandler.Subscribe, so that it is used directly within the process flow. This will give us the greatest control.
It should be the exception, and only when we are handling functionality that can be out of band, that we should subscribe to an event using EventHandler.SubscribeAsync.