Reputation: 518
I was trying to create async api. But the response shows sequential execution.
Steps done: Open the url in two tabs of chrome. And hit them one after other quickly. url ex:- localhost:9000/getStar
.
But the execution log is like :-
[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9000
(Server started, use Ctrl+D to stop and go back to the console...)
[success] Compiled in 107ms
[info] application - Application has started
[info] play - Application started (Dev)
[info] application - Async started ************************** :tarun
[info] application - Success Async call :1
[info] application - Success Async call :2
[info] application - Success Async call :3
[info] application - Success Async call :4
[info] application - Success Async call :5
[info] application - Success Async call :6
[info] application - Success Async call :7
[info] application - Success Async call :8
[info] application - Success Async call :9
[info] application - Async finished ************************** :tarun
[info] application - Async started ************************** :tarun1
[info] application - Success Async call :1
[info] application - Success Async call :2
[info] application - Success Async call :3
[info] application - Success Async call :4
[info] application - Success Async call :5
[info] application - Success Async call :6
[info] application - Success Async call :7
[info] application - Success Async call :8
[info] application - Success Async call :9
[info] application - Async finished ************************** :tarun1
The code for this is :
package controllers
import play.Logger
import play.api.libs.json.Json
import play.api.mvc._
import scala.concurrent.Future
object StarController extends Controller {
import play.api.libs.concurrent.Execution.Implicits.defaultContext
def getStarAsync(name : String) = Action.async{
val futureResult = Future{
Logger.info("Async started ************************** :" + name)
val a = 0;
for( a <- 1 until 10) {
Thread.sleep(1000)
Logger.info("Success Async call :" + a.toString)
}
Logger.info("Async finished ************************** :" + name)
Map("success" -> Json.toJson(true), "msg" -> Json.toJson("Success Async by :" + name), "code" -> Json.toJson(200))
}
futureResult.map{ result =>
Ok(Json.toJson(result))
}
}
}
Can anyone please help me understand , why the execution of the was sequential even with async call ?
Upvotes: 1
Views: 558
Reputation: 181
I did a lot of experiments and found one thing. Maybe it sounds crazy, but Play handles simultaneous requests sequentially only if they are made from the same browser to the same route. If I make requests via curl or from different browsers or even from one browser but to different routes, then they are handled asynchronously. Not sure what kind of protection Play does in such a way, but this protection exists and it's a fact.
Upvotes: 1
Reputation: 16328
Just to clarify m-z's answer. This is example how you could handle some asynchronous collections in your code
def getStarAsyncOld(name: String) = Action.async {
val futureResult = Future {
Logger.info("Async started ************************** :" + name)
} flatMap (_ => Future.sequence(for (a <- 1 until 10) yield Future {
Thread.sleep(1000)
Logger.info("Success Async call :" + a.toString)
})) map { _ =>
Logger.info("Async finished ************************** :" + name)
Map("success" -> Json.toJson(true), "msg" -> Json.toJson("Success Async by :" + name), "code" -> Json.toJson(200))
}
futureResult.map { result =>
Ok(Json.toJson(result))
}
}
or absolutely the same using for
:
def getStarAsync(name: String) = Action.async {
for {
_ <- Future(Logger.info("Async started ************************** :" + name))
_ <- Future.sequence(for (a <- 1 until 10) yield Future {
Thread.sleep(1000)
Logger.info("Success Async call :" + a.toString)
})
_ = Logger.info("Async finished ************************** :" + name)
result = Map("success" -> Json.toJson(true), "msg" -> Json.toJson("Success Async by :" + name), "code" -> Json.toJson(200))
} yield Ok(Json.toJson(result))
}
Upvotes: 0
Reputation: 55569
Action.async
doesn't magically make the controller method asynchronous. The only thing it is different about it is that it expects a Future[Result]
instead of a Result
. That's it. Controllers are otherwise asynchronous as they can be by nature (i.e. a normal Action
gets wrapped in a Future
anyway). The thing here is that Thread.sleep(1000)
blocks it's thread, and is not the least bit asynchronous.
The other thing is that in dev mode (i.e. activator run
), the play server uses a single thread to serve requests, so it can properly handle reload/compile, evolutions, etc. So what's happening is that you're just blocking that thread with synchronous calls. You should see different results using activator start
, but even so, there's no point in using Action.async
here unless you're going to delegate that blocking to a different thread pool.
Upvotes: 8