简体   繁体   English

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

[英]Does every Scala Future eventually have to have Await.result called on it

I am trying to learn Scala futures. 我正在努力学习斯卡拉期货。 As the title suggests, it seems to me that Await.result must eventually be called at some point in your program to get the value of a future extracted in to a Scala field. 正如标题所暗示的那样,在我看来Await.result最终必须在程序中的某个时刻被调用,以便将未来的值提取到Scala字段中。 Here is my code so far. 到目前为止,这是我的代码。 I am querying a arbitrary uri here and mapping it to a case class called Address suppose that this Address case class has one field, final_balance . 我在这里查询任意uri并将其映射到一个名为Address的case类,假设这个Address case类有一个字段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)


  }

Is this incorrect? 这是不正确的?

It is not the only way to get or do something with the underlying value/result from a Future[A] here are some other ways to consider: 它不是获取或使用Future[A]的基础值/结果做某事的唯一方法Future[A]这里有一些其他方法可以考虑:

Combinators 组合子

The point of future combinators is to be able to combine futures together before having to pull the underlying out from the end future. 未来组合者的观点是能够将期货结合在一起,然后才能从最终的未来中拉出底层。 Deferring as long as possible is usually a good idea. 尽可能长时间地推迟通常是一个好主意。

There is a recover combinator which will allow you to do something to deal with an exception occurring: 有一个recover组合器,它可以让你做一些事情来处理异常发生:

f1 recover {
  case e: ApiException => ...do something...
}

There is a fallbackTo combinator too: 还有一个fallbackTo组合器:

f1 fallbackTo f2 // (if result of `f1` is failure case, then execute `f2`)

Now if you wanted the first future that completed to be the result returned, you could use the either combinator though it has interesting characteristics so read the docs and play with it in the REPL some to understand it better: 现在,如果你想要完成的第一个未来是返回的结果,你可以使用either组合器虽然它有一些有趣的特性,所以阅读文档并在REPL中使用它,以便更好地理解它:

f1 either f2

There is also filter , foreach , and the two most obvious map , and flatmap . 还有filterforeach和两个最明显的mapflatmap Another combinator you might want to use is zip which will "zip" results of two futures up as a tuple which you can then apply to a data/value constructor . 您可能想要使用的另一个组合器是zip ,它将两个期货的结果“压缩”为一个元组,然后您可以将其apply数据/值构造函数

Also andThen can be use like this: 还有, andThen可以像这样使用:

f1 andThen f2 // this defines order of execution (f1 and then f2 in this case)

Mapping (the most used combinator IME) 映射(最常用的组合IME)

Yes, this doesn't get the underlying value out, but it can do something with the value: 是的,这不会获得基础价值,但它可以对价值一些事情:

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.

Via Callbacks 通过回调

I don't necessarily recommend this approach, but it's one of the ways you can do something with the result of a Future[A] . 我并不推荐这种方法,但它是你可以一个结果的东西的一种方式Future[A]

One example would be: 一个例子是:

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)
}

Alternatively we can split the behavior out into the success and the failure cases separately: 或者,我们可以将行为分别分为成功案例和失败案例:

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)
}

For Comprehensions & Projections 对于理解和预测

Sometimes you have dependent future values, let's look at this example: 有时你有未来的依赖值,让我们来看看这个例子:

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?

If f1 or f2 failed then you can use the failed projection, something like this: 如果f1f2失败,那么您可以使用失败的投影,如下所示:

val f1: Future[Int] = future {
  throw new RuntimeException("whoops")
}
for (t <- f1.failed) response.error(t)

Await ing Await

Here we have Await.result and Await.ready blocking calls to get the underlying value out from a Future. 在这里,我们有Await.resultAwait.ready阻塞调用以从Future获取底层值。

I don't recommend limiting yourself to Future[A] s. 我不建议将自己局限于Future[A] You should look at Scalaz's Task[A] construct, which I find to be much more flexible, intuitive (to me) and composable. 你应该看看Scalaz的Task[A]构造,我发现它更加灵活,直观(对我来说)和可组合。

Further Reading 进一步阅读

Eventually it must be called though, we cannot pass around a Future[T] forever, right? 最终它必须被召唤,我们永远无法绕过未来[T],对吧?

We can - that is the beauty of Future :) 我们可以 - 这就是未来的美丽:)

Since getFinalBalance , doesn't really need final_balance, let's change the code to return a Future. 由于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
}

Now we're back to having a Future. 现在我们又回到了拥有未来。 What if we want to do something useful? 如果我们想做一些有用的事情怎么办? Let's say we just want just print the balance it stdout. 假设我们只想打印它的标准。 We can describe this using a function with this signature: 我们可以使用具有此签名的函数来描述它:

def printFinalBalance(balance: Future[Double]): Unit

To define this function, we could use Await.result, but we don't have to. 要定义此函数,我们可以使用Await.result,但我们不必这样做。 We can use onComplete to register a call back function to be applied when the Future completes. 我们可以使用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)
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM