Scala for understanding future profitability. How to wait until the future returns?

I have a function that provides a context:

def buildContext(s:String)(request:RequestHeader):Future[Granite.Context] = { .... // returns a Future[Granite.Context] } 

Then I have another function that uses Context to return Option [Library.Document]:

 def getDocument(tag: String):Option[Library.Document] = { val fakeRequest = play.api.test.FakeRequest().withHeaders(CONTENT_TYPE -> "application/json") val context = buildContext(tag)(fakeRequest) val maybeDoc = context.getDocument //getDocument is defined on Granite.Context to return an Option[Library.Document] } 

How would this code be taken into account if the future came back or not? I saw that for / yield was used to wait for a return, but I always assumed that for / yield just put things together and had nothing to do with waiting for Futures to return. I got a little stuck here and really don't ask the right question!

+6
source share
4 answers

Two other answers are misleading. A for yield in Scala is a compiler primitive that converts to map or flatMap . Do not use Await , if you can avoid this, this is not a simple problem.

You enter the locking behavior, and you still have to realize the system damage that you do when locking.

When it comes to Future , map and flatMap do different things:

the card is executed when the future ends. This is an asynchronous way to create safe type mappings.

 val f: Future[A] = someFutureProducer def convertAToB(a: A): B = {..} f map { a => convertAToB(a) } 

flatMap

is what you use for a chain of things:

 someFuture flatMap { _ => { someOtherFuture } } 

The above equivalent:

 for { result1 <- someFuture result2 <- someOtherFuture } yield result2 

On Play, you would use Async to handle the above:

 Async { someFuture.map(i => Ok("Got result: " + i)) } 

Update

I misunderstood your use of Play. However, this does not change anything. You can still make your logic asynchronous.

 someFuture onComplete { case Success(result) => // doSomething case Failure(err) => // log the error etc } 

The main difference is when you asynchronously think that you should always map and flatMap and do everything else inside Future so that everything is done. The increase in productivity is massive.

The larger your application, the greater the gain.

+10
source

When using concepts for Future you do not wait for its completion, you just say: when it is finished, use it like this, and For-assrehension returns another Future in this case.

If you want to wait for the future to end, you should use Await as follows:

 val resultContext = Await.result(context , timeout.duration) 

Then run the getDocument method as such:

 val maybeDoc = resultContext.getDocument 

EDIT

The usual way to work with Futures is to wait until the last moment before you Await . As another answer noted here, the Play Framework does the same, letting you return Future[Result] . Thus, a good way to do something would only be to use for understanding and make your methods return Futures, etc. Until the last moment, when you want to finally return your result.

+5
source

You can use scala.concurrent.Await for this:

 import scala.concurrent.duration._ import scala.concurrent.Await def getDocument(tag: String):Option[Library.Document] = { val fakeRequest = play.api.test.FakeRequest().withHeaders(CONTENT_TYPE -> "application/json") val context = Await.result(buildContext(tag)(fakeRequest), 42.seconds) val maybeDoc = context.getDocument } 

But Await will block the thread until the future is complete, so it’s better to make buildContext synchronous operation that returns Granite.Context , or make an asynchronous getDocument , returning Future[Option[Library.Document]] .

+2
source

Once you find yourself in the future, you must stay in the future, or you must wait until the future comes.

The wait is usually bad because it blocks execution, so you should work in the future.

Basically, you should change your getDocument method to return Future to something like getDocument(tag: String):Future[Option[Library.Document]]

Then, using map ro flatMap , you bind your future calls:

return buildContext(tag)(fakeRequest).map(_.getDocument)

If buildContext fails, the map will wrap Failure

Then call

 getDocument("blah").onComplete { case Success(optionalDoc) => ... case Failure(e) =>... } 
+1
source

All Articles