Why Event.fireAsync() requires an @ObservesAsync annotation?

3.1k views Asked by At

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?
1

There are 1 answers

0
Siliarus On BEST ANSWER

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