Since Netty is a non-blocking server, what effect does changing an action to using .async
?
def index = Action { ... }
versus
def index = Action { ... }
is non-blocking you are right.
The purpose of Action.async
is simply to make it easier to work with Futures
in your actions.
For example:
def index = Action.async {
val allOptionsFuture: Future[List[UserOption]] = optionService.findAll()
allOptionFuture map {
options =>
Ok(views.html.main(options))
}
}
Here my service returns a Future
, and to avoid dealing with extracting the result I just map it to a Future[SimpleResult]
and Action.async
takes care of the rest.
If my service was returning List[UserOption]
directly I could just use Action.apply
, but under the hood it would still be non-blocking.
If you look at Action
source code, you can even see that apply
eventually calls async
:
https://github.com/playframework/playframework/blob/2.3.x/framework/src/play/src/main/scala/play/api/mvc/Action.scala#L432
I happened to come across this question, I like the answer from @vptheron, and I also want to share something I read from book "Reactive Web Applications", which, I think, is also great.
The
Action.async
builder expects to be given a function of typeRequest => Future[Result]
. Actions declared in this fashion are not much different from plainAction { request => ... }
calls, the only difference is that Play knows thatAction.async
actions are already asynchronous, so it doesn’t wrap their contents in a future block.That’s right — Play will by default schedule any Action body to be executed asynchronously against its default web worker pool by wrapping the execution in a future. The only difference between
Action
andAction.async
is that in the second case, we’re taking care of providing an asynchronous computation.
It also presented one sample:
def listFiles = Action { implicit request =>
val files = new java.io.File(".").listFiles
Ok(files.map(_.getName).mkString(", "))
}
which is problematic, given its use of the blocking java.io.File
API.
Here the
java.io.File
API is performing a blocking I/O operation, which means that one of the few threads of Play's web worker pool will be hijacked while the OS figures out the list of files in the execution directory. This is the kind of situation you should avoid at all costs, because it means that the worker pool may run out of threads.
-
The reactive audit tool, available at https://github.com/octo-online/reactive-audit, aims to point out blocking calls in a project.
Hope it helps, too.