欠条可以分为两类:无状态欠条和有状态欠条。

有状态欠条

有状态欠条的类型为Future.Stateful,从Future派生。

有状态欠条表示正在进行中的操作。可以通过valueisCompleted查询操作是否已经成功完成。

有状态欠条可以和scala.concurrent.Future相互隐式转换,但需要提供ExecutionContext隐式参数:

val myStatefulFuture: Future.Stateful[Unit] = Promise.completeWith(Future[Unit] {})
import scala.concurrent.ExecutionContext.Implicits.global
val myConcurrentFuture: scala.concurrent.Future[Unit] = myStatefulFuture
val myStatefulFuture2: Future.Stateful[Unit] = myConcurrentFuture

之所以需要提供ExecutionContext隐式参数,是因为scala.concurrent.Future不支持“线程无关”模型,必须要指定线程池才能运转。

无状态欠条

无状态欠条的类型为Future.Stateless,也从Future派生。

无状态欠条是Stateless Future框架的核心特性,Scala标准库中没有类似的概念。Stateless Future框架之所以叫这个名字,也正是因为其特有的无状态欠条。

初印象一节的例子中出现的Nio2Future,就属于无状态欠条。此外,Future宏生成的也是无状态欠条,例如:

val myFuture: Future.Stateless[Unit] = Future[Unit] {}

无状态欠条中所有API签名都与有状态欠条一致,但是语义却有所不同。而且,相比有状态欠条,无状态欠条中缺少了valueisCompleted方法,无法查询欠条可否兑现(即操作是否完成)。

这是因为,顾名思义,无状态欠条其实根本不保存任何状态,也就没法提供操作进度供查询了。

其实,无状态欠条是懒人开的欠条。懒人在创建无状态欠条时,根本不会发起任何操作。无状态欠条所对应的操作,要到持有欠条一方调用foreach或者onComplete时,懒人才会开始执行。这个特性就是惰性求值。惰性求值正是纯函数式语言的特征之一。尽管无状态欠条的方法签名与有状态欠条大多雷同,但语义却不同于有状态欠条或scala.concurrent.Future,而更像Scala 2.8的Continuation插件或Haskell中的Monad。

无状态欠条的另一特性是重复执行。如果多次调用同一无状态欠条实例中的foreach或者onComplete,那么每次调用都会发起一次新的操作,每次调用所传入的回调函数,都会收到相互独立的操作结果。

无状态欠条可以和scala.Responder相互隐式转换:

val myStatelessFuture: Future.Stateless[Unit] = Future[Unit] {}
val myResponder: Responder = myStatelessFuture
val myStatelessFuture2: Future.Stateless[Unit] = myResponder

两类欠条的相互转换

要想把无状态欠条转换成有状态欠条,应使用Future.Stateful的派生类Promise,而要想把有状态欠条转换成无状态欠条,应使用Future宏包装:

val myStatelessFuture: Future.Stateless[Unit] = Future[Unit] {}
val myStatefulFuture = Promise[Unit]
myStatefulFuture.completeWith(myStatelessFuture)
val myStatelessFuture2: Future.Stateless[Unit] = Future[Unit] { myStatefulFuture.await }

杨博

岂凡 游戏架构师