How to return response immediately to client without waiting for any long computation to finish

35 views
Skip to first unread message

Nir

unread,
Mar 28, 2018, 5:08:38 PM3/28/18
to Play Framework [deprecated]
I see examples on play site

val futurePIValue: Future[Double] = computePIAsynchronously()
val futureResult: Future[Result] = futurePIValue.map { pi =>
  Ok("PI value computed: " + pi)
}

In above example `map` is a blocking call which waits for `futurePIValue` to finish.

Doc also says :

"The web client will be blocked while waiting for the response, but nothing will be blocked on the server, and server resources can be used to serve other clients."

How do I not make web client waiting and instead just return some ID immediately which they an use later to query the result? 

Justin du coeur

unread,
Mar 29, 2018, 8:58:31 AM3/29/18
to play-fr...@googlegroups.com
I don't think there's any trivial way to handle this server-side.  Basically, you would need to wrap the Future inside some sort of object with an ID that you generate, track those IDs/objects, return the ID to the client immediately, and have the client poll the server with that ID to see whether it is done.  It's doable, but a good deal of work to get right.

It's usually not worth all that effort -- instead, you usually just send the query off from the client, and have it do what it needs to when the server responds.  Most web-client libraries have reasonably mature tools for notifying your application code when a request completes.  The only time I've ever needed to do this sort of ID+polling you're describing was when I needed to implement progress bars on the client...

Nir

unread,
Mar 29, 2018, 2:03:00 PM3/29/18
to Play Framework [deprecated]
I see. But this is like internal service tool and server ETL workload. So most of it is batch jobs which could takes minutes to hour.

I found Futures utility class in play which seems to work. Only thing is it throws TimeoutException but job still runs. Might look like a workaround.


import play.api.libs.concurrent.Futures._
futures.timeout(1.seconds) { Future(etlCoordinator.fullRefresh) map {
        u => Ok("FullRefresh job submitted successfully")
      } recover {
      case to: TimeoutException => Ok("FullRefresh job submitted successfully")
      case t: Throwable => InternalServerError("Issue in submitting FullRefresh job")

Miguel Ángel Moreno

unread,
Mar 29, 2018, 2:42:47 PM3/29/18
to Play Framework [deprecated]
Hello Nir

Have you considered to send the message to an actor that runs that computation and/or a queue system?

A request comes => you tell the actor to compute XXX (fire and forget with `!`) => you return 200 Ok back to the browser

You loose feedback this way, I think, but you can handle that situation via logs or how the actor behaves

Best,
Miguel

Nir

unread,
Mar 29, 2018, 7:55:21 PM3/29/18
to Play Framework [deprecated]
Thanks for the reply. I will consider learning actor system in "Future". For now, I am having trouble handling things through series of Futures themselves :)
Reply all
Reply to author
Forward
0 new messages