3

The use case is actually fairly typical. A lot of web services use authorization tokens that you retrieve at the start of a session and you need to send those back on subsequent requests.

I know I can do it like this:

lazy val myData = {    
    val request = ws.url("/some/url").withAuth(user, password, WSAuthScheme.BASIC).withHeaders("Accept" -> "application/json")
    Await.result(request.get().map{x => x.json }, 120.seconds)
}

That just feels wrong as all the docs say never us Await.

Is there a Future/Promise Scala style way of handling this?

I've found .onComplete which allows me to run code upon the completion of a Promise however without using a (mutable) var I see no way of getting a value in that scope into a lazy val in a different scope. Even with a var there is a possible timing issue -- hence the evils of mutable variables :)

Any other way of doing this?

Techmag
  • 1,383
  • 13
  • 24
  • If your goal (not specific to PlayWS) is to get a `T` out of `Future[T]`, declaring it as `lazy val` doesn't change anything for me: either you use `Await` (with the known drawback), or you can't. – cchantep Jun 03 '16 at 16:01
  • The pattern is collect this data once and use it many times -- exactly what a `lazy val` implies. I'm trying to flip things (in my head first) such that once this value is received I gather the rest of what I need in the `.onComplete` scope and finish processing there. Given what needs to be done and "where" it needs to be done (inside an Actor) that approach just might do the trick. – Techmag Jun 03 '16 at 22:15
  • You can have a `Future[T]` as `lazy val`, but not sure it's really useful – cchantep Jun 03 '16 at 22:18
  • FYI: The above code causes some kind of hell within an Actor that I haven't been able to decipher yet. If I call that code via a Controller's Action using `Action.sync {...}` if runs no problem. If however I try to call the exact same code within a Actor trigger by a timer task then the code simply STOPS running. I do not get a lock up I simply get code that falls off a cliff at the fist ws.url() call. Timers continue to run as does the "site" itself and yet the Actor never seems to make an actual web call. Have no idea how to get past this yet... – Techmag Jun 05 '16 at 13:44
  • Scratch that last comment -- http://stackoverflow.com/a/17612613/2162886 -- apparently @Inject at the field level doesn't work unless your class is itself injected. That is forgivable but for it not to scream bloody hell when it fails is not. I had a completely silent null pointer -- no stack trace - NOTHING. Days wasted yet again over spilled Guice... – Techmag Jun 05 '16 at 14:12
  • They latest headache in this constant stream of Play/Akka/Guice pain is that now that I've got this working in `dev` mode it silently fails in `testProd` mode for reasons that are completely unknown. Have moved that question to Google Groups. – Techmag Jun 05 '16 at 17:55

1 Answers1

0

Unfortunately, there is no way to make this non-blocking - lazy vals are designed to be synchronous and to block any thread accessing them until they are completed with a value (internally a lazy val is represented as a simple synchronized block).

A Future/Promise Scala way would be to use a Future[T] or a Promise[T] instead of a val x: T, but that way implies a great deal of overhead with executionContexts and maps upon each use of the val, and more optimal resource utilization may not be worth the decreased readability in all cases, so it may be OK to leave the Await there if you extensively use the value in many parts of your application.

Sergey
  • 2,880
  • 3
  • 19
  • 29