Question

Looking to achieve this:

HTTP request to REST API -> parse -> make async call to another API -> respond to http req with the result of the async call.

Currently, the code looks like:

 def getItems(param: String): LiftResponse = {

    @volatile var resp: LiftResponse = new BadResponse

    param.toLowerCase match {
      case "something" =>
        val req = Async call returning a Future
        req onSuccess {
          case items =>
            resp = new JsonResponse(items map (decompose(_)), S.getResponseHeaders(Nil), S.responseCookies, 200)
        }
        req onFailure {
          case fail => resp = new BadResponse
        }
        resp

      case _ => new OkResponse
    }
  }

But it looks like poor implementation.

What is the idiomatic Scala way to write the above?

Was it helpful?

Solution 3

I think you can try to inline @volatile var resp by:

def getItems(param: String): LiftResponse = {

    param.toLowerCase match {
        case "something" =>
            val req = Async call returning a Future
            req.onComplete {
                case Success(items) => new JsonResponse(items map (decompose(_)), S.getResponseHeaders(Nil), S.responseCookies, 200)
                case Failure(t) => new BadResponse
            }
        case _ => new OkResponse
    }

}

--edit--

sorry, onComplete returns Unit, how about using Await to get the result of future:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import scala.concurrent.duration._

def getItems(param: String): LiftResponse = {

    param.toLowerCase match {
        case "something" =>
            val req = Async call returning a Future
            val response = req map { items => 
                new JsonResponse 
            } recover { 
                case t:Throwable => new BadResponse
            }
            Await.result(response, Duration(100, MILLISECONDS))
        case _ => new OkResponse
    }

}

OTHER TIPS

Your code will probably not do what you think it should since it depends on scheduling whether it returns null or something else. Is LiftResponse a strict value or can it be deferred? If strict then you will have to return a Future[LiftResponse] obtained by mapping your req: Future.

Look into using Lift's RestHelper in conjunction with RestContinuation.async. It supports using continuations to suspend a request until data is available for it. There's some sample code at http://demo.liftweb.net/async_rest . After the async continuation is invoked and before the reply function is called with the result, the request thread will be released to the thread pool. Once the reply function is called, the request will be put back on a thread and the response sent to the client.

Note (3 years later): with the recent (Nov. 2016) release of Lift3, you can use net.liftweb.http._, as described in "Request and Session Access in Lift Futures", from Piotr Dyraga.

As an example, say that you want to lazily render a list of users.
First, you execute database query asynchronously and get Future[Seq[User]] as a result of this operation.
If you use Lift 2, incorporate FutureBinds that I described in one my previous posts or if you use Lift3, import net.liftweb.http._ and do:

val allUsers: Future[Seq[User]] = ... // retrieve users from DB

".user-list-async-container" #> allUsers.map { users =>
  ".user-list" #> users.map { user =>
    ".user" #> userBindings(user)
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top