In CDI 2.0 one can fire an event asynchronously by calling Event.fireAsync()
and then listen to this event by with a @ObservesAsync
annotated listener.
Why do we need both Event.firesAsync()
and @ObservesAsync
?
- Couldn't CDI 2.0 process asynchronously an event fired by
Event.fire()
and caught with @ObservesAsync
?
- Or reversely, why couldn't CDI 2.0 asynchronously process an event fired by
Event.fireAsync()
and caught with @Observes
?
A very good question indeed, here is a bit of insight.
CDI EG (expert group) decided against mixing those two for several reasons:
- Backward compatibility
- Existing applications use synchronous and it needs to behave equally
- Keeping the same annotation would require you to add extra option to differentiate anyway
- Return type
- Calling
Event.fireAsync()
gives you a CompletionStage
to which you can chain next steps with exceptionally()
or thenApply()
etc. This naturally fits into asynchronous programming model.
- Good old
Event.fire()
only gives you void
, to which you cannot react at all - not good for async
- Again, the return value of synchronous one cannot be changed due to backward compatibility
- Excepting handling differs a lot
- Exception in synchronous notification == end of chain, you blow up
- Exception in asynchronous notification == you keep going and gather all exceptions from observer methods (possibly from multiple threads!) and then present them back to invoking code. Since it's
CompletionStage
, you can easily react to that.
- Mixing the two would lead to a very confusing result on user side - when would you blow up and when do you keep going? What is the true outcome of
Event.fire()
(if it were for async as well)
- Internal observer processing
- It would be very complex (assuming it would even be possible) to mix sync and async
- Bear in mind that you need to strictly draw a line between which is going to be sync and async as contexts do not propagate in other threads (for instance
RequestScoped
needs to be re-activated, by Weld, in the async observer thread)
- Similar troubles come with security context propagation for integrators
- There is often pre-processing of observers to make it work really fast, if you have one observer method for both, you cannot really pre-process it as you never know what it will be used for
Other advantages of the current model I can think of:
- Presence of
fireAsync()
allows you to fire event with additional options
- Last but not least - user experience
- This way it is clear, that what you had before works exactly the same
- And that for
fireAsync()
you have matching @ObservesAsync