我有一个提供上下文的功能:
def buildContext(s:String)(request:RequestHeader):Future[Granite.Context] = {
.... // returns a Future[Granite.Context]
}
然后我有另一个函数使用Context来返回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]
}
如果Future已经返回,这段代码将如何考虑?我已经看到/ yield曾经等待返回,但我总是认为for / yield只是将事情平面化并且与等待Futures返回没有任何关系。我有点卡在这里,并没有提出正确的问题!
答案 0 :(得分:10)
另外两个答案具有误导性。 Scala中的for yield
是一个编译器原语,可以转换为map
或flatMap
个链。如果可以避免,请不要使用Await
,这不是一个简单的问题。
你正在引入阻止行为,你还没有意识到阻止时你正在做的系统性伤害。
说到Future
,map
和flatMap
执行不同的操作:
<强>地图 在未来完成时执行。这是一种进行类型安全映射的异步方法。
val f: Future[A] = someFutureProducer
def convertAToB(a: A): B = {..}
f map { a => convertAToB(a) }
<强> flatMap 强>
是你用来连锁的东西:
someFuture flatMap {
_ => {
someOtherFuture
}
}
以上相当于:
for {
result1 <- someFuture
result2 <- someOtherFuture
} yield result2
在Play中,您可以使用Async
来处理上述内容:
Async {
someFuture.map(i => Ok("Got result: " + i))
}
<强>更新强>
我误解了你对Play的使用。不过,它并没有改变任何东西。你仍然可以使你的逻辑异步。
someFuture onComplete {
case Success(result) => // doSomething
case Failure(err) => // log the error etc
}
异步思考的主要区别在于,您始终必须map
和flatMap
并执行Future
内的所有其他操作才能完成任务。性能提升很大。
您的应用越大,收益越大。
答案 1 :(得分:6)
在Future
上使用for-comprehension时,你并没有等到它完成,你只是说:完成后,像这样使用它,并且For-在这种情况下,理解会返回另一个Future
。
如果您想等待将来完成,您应该使用Await
,如下所示:
val resultContext = Await.result(context , timeout.duration)
然后在其上运行getDocument方法:
val maybeDoc = resultContext.getDocument
修改强>
与期货合作的常用方法是等到Await
之前的最后一刻。正如此处的另一个答案所指出的,Play Framework通过允许您返回Future[Result]
来做同样的事情。所以,做一件好事的好方法就是只使用for-comprehensions并让你的方法返回Futures等,直到你想要最终返回结果的最后一刻。
答案 2 :(得分:2)
您可以使用scala.concurrent.Await
:
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
}
但是Await
会在未来未完成时阻止线程,所以最好让buildContext
同步操作返回Granite.Context
,或者使getDocument
异步也返回,返回Future[Option[Library.Document]]
。
答案 3 :(得分:1)
一旦你在将来,你必须留在未来,或者你必须等到未来到来。
等待通常是一个坏主意,因为它阻止了你的执行,所以你应该在将来工作。
基本上您应该更改getDocument
方法,将Future
返回到getDocument(tag: String):Future[Option[Library.Document]]
然后使用map
ro flatMap
,链接未来的电话:
return buildContext(tag)(fakeRequest).map(_.getDocument)
如果buildContext
失败,地图将换行Failure
然后致电
getDocument("blah").onComplete {
case Success(optionalDoc) => ...
case Failure(e) =>...
}