scala用于与Future一起使用的良率理解。如何等到未来回来?

时间:2014-03-14 12:30:39

标签: scala

我有一个提供上下文的功能:

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返回没有任何关系。我有点卡在这里,并没有提出正确的问题!

4 个答案:

答案 0 :(得分:10)

另外两个答案具有误导性。 Scala中的for yield是一个编译器原语,可以转换为mapflatMap个链。如果可以避免,请不要使用Await,这不是一个简单的问题。

你正在引入阻止行为,你还没有意识到阻止时你正在做的系统性伤害。

说到FuturemapflatMap执行不同的操作:

<强>地图 在未来完成时执行。这是一种进行类型安全映射的异步方法。

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
}

异步思考的主要区别在于,您始终必须mapflatMap并执行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) =>...
}