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 ?
Action.async
doesn't magically make the controller method asynchronous. The only thing it is different about it is that it expects aFuture[Result]
instead of aResult
. That's it. Controllers are otherwise asynchronous as they can be by nature (i.e. a normalAction
gets wrapped in aFuture
anyway). The thing here is thatThread.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 usingactivator start
, but even so, there's no point in usingAction.async
here unless you're going to delegate that blocking to a different thread pool.Further reading.
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.
Just to clarify m-z's answer. This is example how you could handle some asynchronous collections in your code
or absolutely the same using
for
: