Future是指尚未完成的操作。在异步编程中,这些操作结果将来会在操作完成后,再传给回调函数。换句话说,一个Future对象,就是一张将来可能兑现的欠条

在Stateless Future框架中,欠条对应类型为com.qifun.statelessFuture.Future[AwaitResult]。其中,AwaitResult类型参数表示,欠条兑现时,将会给付的结果类型。

兑现欠条

有两种语法等待一张欠条兑现,await语法和for推导式1语法。

await等待欠条兑现

await方法的语义是暂停执行流,一直等到欠条兑现才返回,其返回值就是欠条兑现的结果。我在前一节中讲过,await会被Future宏替换成onComplete,并不真正阻塞线程:

Nio2Future.connect(socket, new InetSocketAddress("www.qifun.com", 80)).await
readAll(socket, response).await
writeAll(socket, request).await

在上述例子中,await表示顺序等待操作,必须出现在Future块中。如果在Future外使用await,就会导致`await` must be enclosed in a `Future` block的编译错误。

嵌套函数

需要注意,在Future块中的内嵌函数中,一般也不能用await

val myFuture = Future {
  def nestedFunction() = {
    anotherFuture.await // 编译错误!
  }
  nestedFunction()
}

幸好Future可以嵌套,所以你可以这样写:

val myFuture = Future {
  def nestedFunction() = Future {
    anotherFuture.await // 编译通过
  }
  nestedFunction().await
}

for等待欠条兑现

欠条兑现的另一语法借用了Scala的for关键字:

println("即将开始等待myFuture")
for (result <- myFuture) {
  println(s"结果是:$result")
}
println("正在等待myFuture……")

注意,此处的for语句并不是循环!for代码块内是欠条兑现后才触发的代码,通常只触发一次。而for代码块之后的代码则在开始等待myFuture以后,马上执行。

所以,大多数情况下,上述代码的输出顺序将会类似这样:

即将开始等待myFuture
正在等待myFuture……
结果是:<myFuture兑现后的结果>

等待多张欠条

还可以用for语句顺序等待多张欠条:

println("即将开始等待myFuture1")
for (result1 <- myFuture1; result2 <- getMyFuture2(result1)) {
  println(s"结果是:$result1、$result2")
}
println("正在等待myFuture1和getMyFuture2(result1)……")

上述代码中,两张欠条依次等待。其中第二张欠条getMyFuture2(result1)甚至是利用第一张欠条的兑现结果result1才能计算得到。

欠条的映射和合并

你也可以用for/yield语法,把多张欠条合成一张:

case class MyResult(result1: Result1, result2: Result2)

val myFuture3: Future[MyResult] = for (result1 <- myFuture1; result2 <- getMyFuture2(result1)) yield {
  MyResult(result1, result2)
}

for/yield创建的欠条,属于无状态欠条,支持惰性执行。创建这张新欠条时并不会发起myFuturegetMyFuture2(result1)所对应的异步操作,而要等到对新欠条调用await或者不含yieldfor时,才会发起操作:

for (result3 <- myFuture3) {
  println(s"最终结果是:${result3.result1}、${result3.result2}")
}

当然,你也可以用前面学过的Future/await来合并欠条。以下代码与上方的for/yield示例,功能完全相同:

case class MyResult(result1: Result1, result2: Result2)

val myFuture3: Future[MyResult] = Future[MyResult] {
  val result1 = myFuture1.await
  val result2 = getMyFuture2(result1).await
  MyResult(result1, result2)
}

(待续。下一节将会讲解无状态欠条和有状态欠条的区别。)


  1. for推导式For Comprehension。Scala的for推导式语法类似命令式语言的for循环,但语义却更接近Ruby、Python等语言的列表推导式(List Comprehension)。


杨博

岂凡 游戏架构师