创造未来而不启动它



这是我上一个问题的后续

假设我想用我的函数创建一个未来,但不想立即启动它(即我不想调用val f = Future { ... // my function}

现在我看到可以这样做:

<>之前val p = promise[单位]Val f = p.future map{_ =>//我这里的函数}之前

这是唯一的方法来创建一个未来与我的函数不执行它吗?

你可以这样做

val p = Promise[Unit]()
val f = p.future
//... some code run at a later time
p.success {
// your function
}

后来编辑:

我认为你正在寻找的模式可以像这样封装:

class LatentComputation[T](f: => T) {
  private val p = Promise[T]()
  def trigger() { p.success(f) }
  def future: Future[T] = p.future
}
object LatentComputation {
  def apply[T](f: => T) = new LatentComputation(f)
}

你可以这样使用:

val comp = LatentComputation {
// your code to be executed later
}
val f = comp.future
// somewhere else in the code
comp.trigger()

你总是可以用闭包延迟创建,你不会马上得到未来的对象,但是你得到一个句柄以后调用。

type DeferredComputation[T,R] = T => Future[R]
def deferredCall[T,R](futureBody: T => R): DeferredComputation[T,R] =
  t => future {futureBody(t)}
def deferredResult[R](futureBody: => R): DeferredComputation[Unit,R] =
  _ => future {futureBody}

如果你太喜欢执行控制,也许你应该使用演员代替?

或者,也许,您应该使用Promise而不是Future: Promise可以传递给其他人,而您保留它以在以后的时间"完成"它。

同样值得为Promise.completeWith做个宣传。

你已经知道如何使用p.future onComplete mystuff

你可以使用p completeWith f从另一个未来触发。

你也可以定义一个创建并返回Future的函数,然后调用它:

val double = (value: Int) => {
  val f = Future { Thread.sleep(1000); value * 2 }
  f.onComplete(x => println(s"Future return: $x"))
  f
}
println("Before future.")
double(2)
println("After future is called, but as the future takes 1 sec to run, it will be printed before.")

我用这个来执行n个批次的期货,比如:

// The functions that returns the future.
val double = (i: Int) => {
  val future = Future ({
    println(s"Start task $i")
    Thread.sleep(1000)
    i * 2
  })
  future.onComplete(_ => {
    println(s"Task $i ended")
  })
  future
}
val numbers = 1 to 20
numbers
  .map(i => (i, double))
  .grouped(5)
  .foreach(batch => {
    val result = Await.result( Future.sequence(batch.map{ case (i, callback) => callback(i) }), 5.minutes )
    println(result)
  })

或者只是使用返回未来的常规方法,并使用诸如for comprehension(顺序调用站点评估)之类的东西连续触发它们

这是标准库未来的一个众所周知的问题:它们的设计方式不是引用透明的,因为它们迫切地求值并记住结果。在大多数用例中,这完全没问题,Scala开发人员很少需要创建非求值的future。

取下面的程序:

val x = Future(...); f(x, x) 

不是同一个程序
f(Future(...), Future(...))

,因为在第一种情况下,future被求值一次,在第二种情况下,它被求值两次。

这些库提供了必要的抽象来处理引用透明的异步任务,这些任务的求值是延迟的,除非开发人员明确要求,否则不会被记忆。

    <
  1. Scalaz任务/gh><
  2. Monix任务/gh>
  3. fs2

如果你想使用猫,猫的效果在Monix和fs2中都能很好地工作。

这是一个小技巧,因为它与未来的工作方式无关,但只需添加lazy就足够了:lazy val f = Future { ... // my function}但请注意,这也是一种类型更改,因为无论何时引用它,您都需要将引用声明为lazy,否则它将被执行。

最新更新