[2.0.3 Scala] Calling a Webservice asynchronously

191 views
Skip to first unread message

Ant Kutschera

unread,
Oct 22, 2012, 5:05:57 PM10/22/12
to play-fr...@googlegroups.com

I am calling a web service as shown in the docs under "The Play WS API" (http://scala.playframework.org/documentation/2.0.4/ScalaWS)

The example is:

    def feedTitle(feedUrl: String) = Action {
      Async {
        WS.url(feedUrl).get().map { response =>
          Ok("Feed title: " + (response.json \ "title").as[String])
        }
      } 
    }

Is the "Async" function required because:

1) WS.url(...).get returns a Promise, and Play needs a way to wait for the response,
2) Because preparing to call a remote site takes a long time and it would be nice to free up the threads handling incoming requests, so that no browser is blocked during this preparation time, or
3) not really needed?

I'd guess at 1, but I'm not really sure... And the API docs don't give anything useful for play.api.mvc.Results#Async

Thanks,
Ant
 

Nick Fisher

unread,
Oct 22, 2012, 9:18:21 PM10/22/12
to play-fr...@googlegroups.com
It's 1.  The expected return type from an Action is a sub-type of Result.  Promise[Result] is not a sub-type of result, so you must wrap that in an AsyncResult.

Ant Kutschera

unread,
Oct 23, 2012, 2:46:11 AM10/23/12
to play-fr...@googlegroups.com
I started looking at the source code, and indeed the Async block simply wraps the Result into a Promise.

I started looking for how the promise is handled, and all I could find was the following snippet in the PlayDefaultUpstreamHandler:


  override def messageReceived(ctx: ChannelHandlerContext, e: MessageEvent) {

       ...

        val response = new Response {

          def handle(result: Result) {
            result match {

              case AsyncResult(p) => p.extend1 {
                case Redeemed(v) => handle(v)
                case Thrown(e) => {
                  server.applicationProvider.get match {
                    case Right(app) => handle(app.handleError(requestHeader, e))
                    case Left(_) => handle(Results.InternalServerError)
                  }
                }
              }
             ...

So if the result is ready, its handled.  But what I can't find is what happens if the result is not yet ready.  I presume its stuck back on a queue and checked later.  But what happens if there are no other requests to handle... does the Play server run in a continuous while loop checking the result and heating the CPU?  Or does it poll every X milliseconds?  Or is it truly a callback, which WS.url(...).get calls, when the result comes in?

Cheers for any help!
Ant

Nick Fisher

unread,
Oct 23, 2012, 7:18:51 PM10/23/12
to play-fr...@googlegroups.com
Promises execute on one of the threads in the thread-pool used by Play's underlying Akka actor system.  They are true callbacks in the sense that they will call your callback function immediately upon completion, no continuous-polling needed.  The only real polling going on is in the HashedWheelTimer implementation used to do Akka's scheduling (this happens even when your Play app isn't doing anything).  When you set a timeout on your Promises (as is the case with WS.url(...).get)) a one-time request is added to the Akka system's scheduler, which will check whether or not the Promise has already been completed, and throw an exception if it has not.  The HashedWheelTimer polls every X number of milliseconds to see if it has any scheduled tasks, and runs any that are past due.

Ant Kutschera

unread,
Oct 24, 2012, 2:28:05 PM10/24/12
to play-fr...@googlegroups.com
Thanks Nick!
Reply all
Reply to author
Forward
0 new messages