Generally speaking, asynchronous programming is applicable to problems where the programmer has to deal with multiple tasks which can take an arbitrarily long time to run. This may be down to the tasks in question requiring a significant amount of computation or needing to interact with a remote system. In a naive programming approach these tasks might be wrapped inside standard functions. A function call can then be issued by the application when the task is to start and it will return to the application once the task is complete.
This naive approach suffers from the fairly obvious problem that the application is blocked from further execution until the long running task is complete. It also prevents multiple such tasks from executing concurrently. More realistic implementations would make use of an additional thread to process the long running task. This allows the application to continue execution and even start up additional long running tasks in parallel with the first. The application may then periodically poll the long running tasks to check for completion.
The model of farming out long running tasks and then either polling for or waiting on completion is a common design pattern - so much so that it has been formalised in the Java 5.0 concurrency library using the Callable and Future interfaces.
The reaction framework takes a different approach to handling task completion in that it makes use of the callback idiom, whereby long running tasks issue callbacks to the application on completion. This eliminates the need for the application to poll for or wait for the task to complete. Callbacks are commonly used in programming languages such as C/C++ which provide native function pointers, but are rarer in Java based applications due to the lack of function pointer support. That said, it is actually quite easy to implement Java based callbacks by defining a suitable callback interface, as shown in Listing 1.1.
Given an application object which implements this interface, it is then possible to support callbacks from long running tasks by passing a reference to the callback interface when the task is started up, as shown in Listing 1.2.
On completing execution, the long running task then only needs to make a call to callback.onCallback(result) in order to pass the result of executing the task back to the application.
As can be seen from the preceding example, using callbacks to indicate completion of long running tasks can provide a very elegant API. However, this does not address the underlying threading and synchronisation issues which are inherent in a callback based design. To make such a programming model work it therefore becomes necessary to provide an underlying framework which hides these complexities from the application developer.