Just because you might use Action.async
, doesn’t automatically mean you’re not blocking. It all depends on whether you’re using blocking API or not.
Play 2.2 seems to work the same way as Play 2.3 in this manner. There isn’t really a difference between Action.apply
and Action.async
, other than their signatures. Action.async
expects some block of code that returns a Future[Result]
, while Action.apply
expects a block of code that returns a Result
. Action.apply
converts that block: => Result
into Future[Result]
by simply calling Future.successful(block)
. (There is a little more work that goes on before calling Future.successful
, but this is the gist of it.)
So the use cases for each boil down to the API you’re using. For example JDBC vs ScalikeJDBC-async, blocking vs. non-blocking database APIs. Let’s say you’re fetching a user from the database and sending it back to the client as json.
The signature of a typical JDBC-backed function might look like this (ignoring failures to simplify):
def read(id: Long): User
Your controller function might then look like this:
def read(id: Long) = Action {
Ok(Json.toJson(User.read(id))
}
This is roughly equivalent to what Action.apply
does:
def read(id: Long) = Action.async {
Future.successful(Ok(Json.toJson(User.read(id)))
}
User.read
is still a blocking JDBC call, however, so this is no better than before.
Now let’s say we’re using an asynchronous DB call that looks something like this:
def read(id: Long): Future[User]
The controller function would look something like this:
def read(id: Long) = Action.async {
User.read(id).map(user => Ok(Json.toJson(user)))
}
Think of it as more of a helper for using APIs that return Future
s. The real benefits come from actual asynchronous implementations of those APIs. If you’re stuck with blocking API (probably JDBC), there are other ways to manage that as well. This thread on the Play mailing list is a good read on the subject: https://groups.google.com/forum/#!topic/play-framework/WWQ0HeLDOjg