Introduction

One of the rich and beautiful features provided by Scala programming language is the Scala Future . For the beginners to introduce futures simply, the Future allows you to write a code snippet which completes and produces the response sometime in the future.

Well, although Futures allow you to write rich, non-blocking and concurrent code, it is very much important to know how to use Futures in different styles (or in other words, in different contexts) to make an effective code.

In this post, I'm going to discuss How to write an effective Scala Future in different use cases.

During my scala journey, I have observed how many people are quite habituated or sometimes mistakenly implement futures using just a Future() but unfortunately, this may not result an effective future all the time.

Future.apply() or Future()

Future( ... <some_code> ... )

This style of code compiles well, gives you a future result. But... this one perhaps creates a separate thread to execute the block asynchronously. This comes at the cost of additional memory allocations, context switches, and synchronization among threads. An individual result may be fractionally slower when executed asynchronously.

However, Future blocks are the best fit to use when you need to process a network call (can be an external service), when you want to interact with database or any other scenario where the block/function needs some time to process and come back.

Future.successful()

Future.successful(... <some_code> ... )

And now, In the other hand we many times encounter some situations where we need to send some default future results or already processed results as a future result. Few such instances can be when you want to send your custom error response object (as a successful response) instead of throwing the exact error back to the rest client when something wrong happens with your rest api.

In such scenarios where the value has been already computed and should go back as a future value, you should not go for a Future block (because of it's overhead as discussed above) rather, Future.successful should be the right fit there as runs with in the same thread and wraps the response with a Future

def getSupportRepresentativeEmail(id: String) = Action.async { request => if(!"".equals(id) && id.length == 10L) { val result: Future[SupportRepresent] = getSupportRepresent(id) result.map(representativeInfo => Ok(representativeInfo.emailId)) } else Future.successful(Ok("support@company.com")) }

But point to be noted here is, The Future.successful runs on the current thread and blocks the current thread until it completes. So it is most appropriate to use Future.successful with a literal value rather using it contact a database server or any other complex operations like that

Future.failed()

Like Future.successful , the Future.failed also run on the current thread and wraps the exception with a future.

So, When you encounter any situation, where you need to throw back a direct exception from the code with in the future instead of any further processing, Future.failed would be the right fit there!. However in the situations where you don't know which exception to throw back and exception may actually occur sometime in future, that piece of code can be moved with in the Future block. Future.failed takes any Throwable type object.

Example Situation: In the same above example, If you want to throw back exception instead of sending the default support email when request sends an invalid id, you can use the Future.failed as shown below

def getSupportRepresentativeEmail(id: String) = Action.async { request => if(!"".equals(id) && id.length == 10L) { val result: Future[SupportRepresent] = getSupportRepresent(id) result.map(representativeInfo => Ok(representativeInfo.emailId)) } else Future.failed(new IllegalArgumentException("Invalid Support Representative Id.")) }

Conclusion

Use Future.apply() or simply Future() (i.e., Future block) in the situations where something to be done asynchronously that can complete sometime in future and may deal with some time consuming operations such as network calls, database operations communicate with one or many other services, processing huge data by consuming multiple cores and etc. Use Future.successful when a literal or already computed value to be passed back as a successful future response Use Future.failed when a known and literal exception to be thrown back without performing any further actions with in the future.