我正在努力学习斯卡拉期货。正如标题所示,在我看来,Await.result
最终必须在程序中的某个时刻被调用,以便将未来的值提取到Scala字段中。到目前为止,这是我的代码。我在这里查询任意uri并将其映射到名为Address
的案例类,假设此Address
案例类有一个字段final_balance
。
case class Address(final_balance:Double)
def getAddress(bitcoinAddress: String)(format: DataFormat): Future[Address] = {
val uri: String = "http://example.com"
val response: Future[HttpResponse] = (IO(Http) ? HttpRequest(GET, Uri(uri))).mapTo[HttpResponse]
implicit val addressJsonFormat = AddressJsonProtocol.addressFormat
val address: Future[Address] = response.flatMap { x =>
x match {
case HttpResponse(_, entity, _, _) =>
Future(entity.asString.parseJson.convertTo[Address])
}
}
address
}
def getFinalBalance(address: String): Double = {
val futureAddress: Future[Address] = QueryAddress.getAddress(address)(Json)
val finalBalance:Future[Double] = for { a <- futureAddress } yield a.final_balance
//block until the result can be determined
Await.result(finalBalance, 15.second)
}
这是不正确的?
答案 0 :(得分:11)
这不是获取或使用Future[A]
的基础值/结果的唯一方法。这里有一些其他方法可以考虑:
<强>组合子强>
未来组合者的目的是能够将期货结合在一起,然后才能从最终的未来中拉出底层。尽可能长时间地推迟通常是一个好主意。
有一个recover
组合器可以让你做一些事情来处理发生的异常:
f1 recover {
case e: ApiException => ...do something...
}
还有一个fallbackTo
组合子:
f1 fallbackTo f2 // (if result of `f1` is failure case, then execute `f2`)
现在,如果你想要完成的第一个未来是返回的结果,你可以使用either
组合器虽然它有一些有趣的特性,所以阅读文档并在REPL中使用它,以便更好地理解它:
f1 either f2
还有filter
,foreach
,以及最明显的两个map
和flatmap
。您可能想要使用的另一个组合器是zip
,它会将两个期货的结果“压缩”为一个元组,然后您可以apply
到数据/值构造函数。 / p>
andThen
也可以像这样使用:
f1 andThen f2 // this defines order of execution (f1 and then f2 in this case)
映射(最常用的组合IME)
是的,这不会获得潜在的价值,但它可以做具有价值的东西:
val fUsers: Future[Int] = future {
api.getConcurrentUsers
}
val fShouldCelebrate: Future[Boolean] = fUsers map { userCount =>
(userCount >= 1000000)
}
// pass fShouldCelebrate around and use a method for extracting the underlying out when
// absolutely needed.
通过回调
我不一定会推荐这种方法,但这是
的结果的方法之一。一个例子是:
Future[A]
或者,我们可以将行为分别分为成功案例和失败案例:
import scala.util.{Success, Failure}
// Not sure what a Response type is...but use your imagination :)
val response: Response = ???
val f: Future[List[String]] = future {
db.getRecentPosts
}
f onComplete {
case Success(posts) => for (p <- posts) response.render(p)
case Failure(t) => response.error("An error has occured: " + t.getMessage)
}
对于理解&amp;突起强>
有时你有未来的依赖值,让我们来看看这个例子:
import scala.util.{Success, Failure}
// Not sure what a Response type is...but use your imagination :)
val response: Response = ???
val f: Future[List[String]] = future {
db.getRecentPosts
}
f onSuccess {
case posts => for (p <- posts) response.render(p)
}
f onFailure {
case t => response.error("An error has occured: " + t.getMessage)
}
如果val ticker: String = "XYZA" // hopefully not a real ticker
val f1: Future[TickerQuote] = future { exchange1.getQuote(ticker) }
val f2: Future[TickerQuote] = future { exchange2.getQuote(ticker) }
val trade = for {
q1 <- f1
q2 <- f2
if (q1 < q2)
} yield exchange1.buy(ticker, 100)
// obviously this is silly but hopefully you get the point?
或f1
失败,那么您可以使用失败的投影,如下所示:
f2
<强> val f1: Future[Int] = future {
throw new RuntimeException("whoops")
}
for (t <- f1.failed) response.error(t)
荷兰国际集团强>
此处我们有Await
和Await.result
阻止调用以从Future获取基础值。
我不建议将自己限制在Await.ready
。你应该看看Scalaz的Future[A]
构造,我觉得它更灵活,更直观(对我来说)和可组合。
进一步阅读
Task[A]
;)我根本不反对。)Task[A]
答案 1 :(得分:4)
最终它必须被调用,我们不能永远传递未来[T],对吗?
我们可以 - 这就是未来的美丽:)
由于getFinalBalance
,并不真正需要final_balance,让我们更改代码以返回Future。
def getFinalBalance(address: String): Future[Double] = {
val futureAddress: Future[Address] = QueryAddress.getAddress(address)(Json)
for { a <- futureAddress } yield a.final_balance
}
现在我们又回到了拥有未来。如果我们想做一些有用的事情怎么办?假设我们只想打印它的标准。我们可以使用具有此签名的函数来描述它:
def printFinalBalance(balance: Future[Double]): Unit
要定义此函数,我们可以使用Await.result,但我们不必这样做。我们可以使用onComplete来注册一个在Future完成时应用的回调函数。
def printFinalBalance(fb: Future[Double]): Unit =
fb onComplete {
case Success(balance) => println("Balance: " + balance)
case Failure(e) => println("An error has occured: " + e.getMessage)
}