【问题标题】:Do Futures always end up not returning anything?期货是否总是最终不返回任何东西?
【发布时间】:2015-09-10 15:51:32
【问题描述】:

鉴于我们必须避免...

1) 修改状态 2) 阻塞

...Future 的正确端到端用法是什么?

使用 Futures 的一般做法似乎是通过使用 map、flatMap 等将它们转换为其他 Futures,但永远创建 Futures 并不好。

是否总是会在某处调用 onComplete,方法将 Future 的结果写入应用程序外部的某处(例如 Web 套接字;控制台;消息代理),或者是否有非阻塞方式访问结果?

Scaladocs 中有关 Futures 的所有信息 - http://docs.scala-lang.org/overviews/core/futures.html 似乎最终都会写入控制台。 onComplete 不返回任何内容,因此我们可能不得不最终执行一些“即发即弃”的 IO。

例如致电println

f onComplete {
  case Success(number) => println(number)
  case Failure(err) => println("An error has occured: " + err.getMessage)
}

但是在更复杂的情况下,我们希望对 Future 的结果做更多​​的事情呢?

例如,在 Play 框架中,Action.async 可以返回一个 Future[Result] 并且框架处理其余部分。它最终会不会期望永远不会从未来得到结果?

我们知道用户需要返回一个Result,那么框架如何仅使用一个 Unit 方法来做到这一点?

是否有一种非阻塞的方式来检索未来的值并在应用程序的其他地方使用它,还是调用 Await 是不可避免的?

【问题讨论】:

  • result (goo.gl/GeW9Pl) 不返回Unit。您必须将Future 视为在不同线程上生成计算的一种方式(我知道我过于简单化了)。
  • 不使用等待阻塞?或者我们总是不得不在某个地方阻塞一个线程的想法。
  • 等待被阻塞。我只是说一种方法是等待计算完成,但同时您可以做其他事情或在未完成的 Future 上定义计算。

标签: scala


【解决方案1】:

最佳做法是使用回调,例如 onCompleteonSuccessonFailure 进行副作用操作,例如日志记录、监控、I/O。

如果您需要继续使用 Future 计算的结果而不是执行副作用操作,则应使用 map 访问计算结果并对其进行组合。

【讨论】:

  • 地图只会产生另一个未来,那么你会怎么做呢?我们最终必须阻止和等待未来的结果的想法是什么?或者我们是否使用有用的 Unit 方法注册回调,例如发送消息(听起来很现实,例如使用 Akka)或修改某些状态(听起来不像函数式编程)?
  • @user2232887 当然最后你必须等待计算完成,然后做一些副作用,比如输出结果,否则这样做有什么意义呢?这并非特定于 Futures。
  • 一般来说,你不希望在你的代码中使用 Awaits(虽然它们有时可能有用,但它是一个例外)。 futures 的全部意义在于避免阻塞在你的控制流中,所以一旦你开始使用它们,它们往往会在你的代码库中传播(因为你的函数自然会返回Future[Result])。任何程序的最终结果都是 I/O,而 I/O 根据定义是副作用(它不会转换您的数据)。通常,虽然我们在 http 的 I/O 情况下使用诸如 Spray 之类的库,所以您的代码中没有显式回调,只是一个返回 HTTP 状态的映射。
  • 谢谢@Tim 我已经做了更多的思考,因为 I/O 必须发生,即降压必须停止在某个地方!尽管 println 没有返回任何内容,但它仍在某处(控制台)操纵一些有用的状态!。如果我们将 println 调用替换为写入某处的套接字。我可以看到它如何更有用。特别是使用 Web 框架!我认为我过于关注应用程序中的状态 within。我在这方面是正确的吗?
  • 我想是的。从技术上讲,I/O 仍然发生在您的应用程序中,但它的抽象层太多,很容易忘记它。
【解决方案2】:

Future 返回一个单位,是的。那是因为它是一个异步触发器。您需要注册回调才能收集结果。

来自您引用的 scaladoc(使用我的 cmets):

// first assign the future with expected return type to a variable.
val f: Future[List[String]] = Future {
  session.getRecentPosts
}
// immediately register the callbacks
f onFailure {
  case t => println("An error has occurred: " + t.getMessage)
}
f onSuccess {
  case posts => for (post <- posts) println(post) 
}

或者代替 println-ing 你可以对结果做一些事情:

f onSuccess {
  case posts: List[String] => someFunction(posts)
}

【讨论】:

  • 我认为我在 someFunction 的实际应用中最挣扎。 API 声明这必须是一个 Unit 方法,但这些方法不是必须执行某种即发即弃的操作(例如发送消息)或修改某些外部状态吗?
  • someFunction 不必返回 Unit。你在哪里看到的?
  • 对不起,我的意思是 someFunction 的返回值会丢失;因为 onSuccess 不能返回任何东西 BUT 我想我太专注于 println 是“无用的”。我的新理解:println 正在写入控制台。即应用程序正在通过在某处写入一些 data 来做一些有用的事情。 “状态”就在我们的应用程序之外。我想,在 Play 框架的情况下,可能有一个 onComplete 方法不需要返回任何东西,它只需要将结果写在更“有用”的地方,例如TCP 套接字。这听起来有道理吗?
  • 没错。 onComplete、onSuccess、onFailure 内部发生的任何事情都会影响状态。它只需要访问您的控制台、TCP 套接字、执行上下文或您想要将结果交给的任何东西。如果您提供有关您想要对结果做什么的更多详细信息,您可能会得到更有帮助的答案。
【解决方案3】:

试试这个:

import scala.concurrent.duration._
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global

val f: Future[Int] = Future { 43 }

val result: Int = Await.result(f, 0 nanos)

那么这里发生了什么?

您正在定义要在不同的thread 上执行的计算。

所以你Future { 43 } 立即返回。

然后您可以等待它并收集结果(通过Await.result)或在其上定义计算而不等待它完成(通过map 等...)

【讨论】:

    【解决方案4】:

    实际上,您所说的 Future 是用于副作用的。 Future 返回的结果取决于它的类型:

    val f = Future[Int] { 42 }
    

    例如,我可以将 Future[Int] 的结果发送到另一个 Future :

    val f2 = f.flatMap(integer => Future{ println(integer) }) // may print 42
    

    如您所知,未来是一个同时发生的过程。所以你可以在未来得到它的结果(也就是使用onComplete等方法)或者通过显式阻塞当前线程直到它得到一个值:

    import scala.concurrent.Await
    import akka.util.Timeout
    import scala.concurrent.duration._
    
    implicit val timeout = Timeout(5 seconds)
    val integer = Await.result(Future { 42 }, timeout.duration)
    

    通常当您开始处理异步流程时,您必须考虑可能永远不会发生的反应。使用链式 Futures 就像声明一个可能随时中断的事件链。因此,等待 Future 的值绝对不是一个好习惯,因为你可能永远也得不到它:

    val integer = Await.result(Future { throw new RuntimeException() }, timeout.duration) // will throw an uncaught exception
    

    尝试更多地从事件的角度来思考,而不是在程序方面。

    【讨论】:

    • 我知道我们可以使用 map、flatMap 等将 Future 转换为另一个 Future。但是我们是否不可避免地必须调用 Unit 方法来结束?获得未来实际结果的正确方法是什么?
    • 如果您需要更多说明,请告诉我。
    猜你喜欢
    • 1970-01-01
    • 2023-03-28
    • 2011-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多