The Future is Now: Asynchronous programming in Java.
An introduction to asynchronous programming, in Java.
“If my calculations are correct….”
Typically, when writing code, we write it synchronously. This means one instruction must complete, before the next is executed.This is obviously pretty important: if it wasn’t, then the snippet below
could yield 0, not 1!
This code will always print out 1, but imagine instead of incrementing 1, we were fetching a value from a database. This could take a bit of time, if we have a lot of data. The print statement could be reached before the number is incremented, if we don’t tell it to wait.
That could be like sending a package to your friend across the country, and then calling them to pick it up. He’ll check his mail and instantly be disappointed when there is nothing there.
Java has an interface called Future made just for this. Using futures is pretty easy! It looks something like this:
This code will make an asynchronous call, wait until it is resolved, and then extract it’s value. This example is pretty simple, but as we try to do more complicated work, the code can get pretty messy.
Note: the while block is not necessary, but it is used to show how the code will run: line 5 cannot execute until the future is completed. Our code is being blocked by it — nothing else will be able to execute until the future is resolved.
So what are promises?
Luckily, we have something called a promise. A promise is saying that we don’t have this value yet, but we promise will at some point. Promises combined with concurrent programming can be very powerful.This means other tasks can be executed without waiting for the current task to resolve.
Now, rather than your friend going to his front door and not finding the package, he can wait for it to get there, and maybe even do other things while he waits.
Luckily for us, engineers at Google made Guava which includes a class that inherits Futures, called ListenableFuture. ListenableFuture can be executed when the computation is done, or immediately if the computation has already completed.
It’s named ListenableFuture because it is just a Future with a listener attached. This is what makes a ListenableFuture so powerful. Instead of having to wait until the promise is resolved, our program can now attend other tasks, and then come back to the original task when it’s ready.
If we take the above example and use ListenableFuture instead of Future, it would look something like this.
However, using .get() on a future is slow. Check below for more efficient methods of extracting a value, such as using transformations.
- Transformations: takes an async variable, resolves it, and returns a new Listenable Future. An example of this is extracting a value out of an object that was asynchronously retrieved.
- Resolve many promises: use all as list to wait until all promises are resolved before executing. There are also variations, such as all successful as list, which will replaces promises that failed with null.
- And many more! There’s many other functions ListenableFutures have, such as immediately failing, or being able to fallback if there is an exception.
The code above can retrieve all of the keys from a map, asynchronously. This is so much better than having to block the rest of your code, by waiting for future to resolve. The ListenableFuture class may seem overwhelming at first, but once you get the hang of it, you’ll never want to stop using it!