每个Scala Future最终都必须调用Await.result

时间:2014-10-29 00:39:25

标签: scala spray

我正在努力学习斯卡拉期货。正如标题所示,在我看来,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)


  }

这是不正确的?

2 个答案:

答案 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

还有filterforeach,以及最明显的两个mapflatmap。您可能想要使用的另一个组合器是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) 荷兰国际集团

此处我们有AwaitAwait.result阻止调用以从Future获取基础值。

我不建议将自己限制在Await.ready。你应该看看Scalaz的Future[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)
  }