这篇一直想不到如何动笔,这段时间刚好做了一些性能优化的工作,而后还有更多.遂提笔权当记录.
“五年,凭此戒娶xxx”
五年,一个人生命中的1/16.没有人会傻到五年一直在等待一个约定.一个人要生存,首先要学会呼吸,吃饭,穿衣,行走.五年之后也许他/她已忘了这个约定,但时间不会为这个承诺停留哪怕停留一秒钟.
scala在不必要的时候不要使用Await进行等待,除非你想 真的做一个傻瓜.
//Don't do it
def waitNotForget = {
def marry(p: Person) = ....
val promise1 = Future(marry)
val result = Await.result(promise1, (5 * 365 + 1) days)
}
做一个聪明的人,首先把承诺放在一边吧,给它一个时间,然后静静等待他的结果.
def waitNotForget = {
def marry(p: Person) = ....
val promise1 = Future(marry)
val system = ActorSysem()
import scala.concurrent.duration._
firstCompletedOf(promise1, waitAPromise((5 * 365 + 1) days))
}
object FutureUtils {
def waitAPromise[T](promise: Future[T], timeout: Duration)(implicit system: ActorSystem): Future[T] = {
val futureTimeout: Future[T] = akka.pattern.after(timeout milli, system.scheduler)(Future.failed(throw new TimeoutException("just a joke!")))
firstCompletedOf(promise, futureTimeout)
}
def firstCompletedOf[T](f0: Future[T], f1: Future[T]): Future[T] = {
val p = Promise[T]()
f0.onComplete(p.tryComplete)
f1.onComplete(p.tryComplete)
p.future
}
}
这里Future可以提前实现,如果实现的话返回一个marry方法执行的结果,如果直到5年这个方法还未执行完毕,这时会抛出TimeoutException("just a joke!"))
异常.
十年
要带你去看周杰伦的演唱会
要有我们的家
要带你走遍中国
要…
热恋的人们其实有一种默契,对于一些头脑发热时说的话,一首曲子在尾音还没有落下时便已消散.
但程序员的浪漫从来不纠结于此,那毫秒级的精确,才是属于程序员浪漫的Blue Bossa.
很多情况下,对于多个Future的处理并非那么轻松,对于这种情况,可以使用以下方案
executeTimeoutSequence(
List(
Future( watchAConcert("周杰伦")),
Future(makeHome(this, u)),
Future(travel(China)
),
(10 * 365 + 2) days)
object FutureUtils {
...
def firstCompletedOf[T](f0: Future[T], f1: Future[T]): Future[T] = ....
def executeTimeoutSequence[T](futureList: List[Future[T]], futureTimeout: Future[T]): List[Future[T]] = {
futureList.map{interFuture => firstCompletedOf(interFuture, futureTimeout)}
}
def executeTimeoutSequence[T](futureList: List[Future[T]], timeout: Duration)(implicit system: ActorSystem): List[Future[T]] = {
val futureTimeout: Future[T] = akka.pattern.after(timeout, system.scheduler)(Future.failed(throw new TimeoutException()))
futureList.map{interFuture =>firstCompletedOf(interFuture, futureTimeout)}
}
}
这里返回的结果是一个List[Future[T]],对于每一个Future的结果返回都可能存在完成
和超时
两种状态之一.例如值完成了周杰伦的演唱会,最终结果是 List[ Future.success(watchAConcert("周杰伦")), Future.failed(throw new TimeoutException(...)), , Future.failed(throw new TimeoutException(...)) ]
比起头脑发热的胡话,更可怕的是清醒的时候说胡话.信誓旦旦为自己的愚蠢列出一些列详细的计划,在真正用代码记录这种带有着强烈实际规划的Future才发现这是一件多么痛苦的事情.
五年 娶你
十年 家
十五年 环游中国
具体来看,这一系列的计划有着明显的时间特征.
首先得完成第一阶段的五年计划,如果第一阶段的五年计划不能完成,则后续的计划全部失败.
同理,在五年内完成第一阶段的计划后,第二阶段计划需要在十年内完成,如果不能完成,则依然是一个Future.failed(TimeoutException())
这种分阶段的异步处理实际处理看似很复杂,相信如果一个真正浪漫的程序员依然可以用代码淡然记录这一切.
Tips
该方法在scala 2.12提供了方法def transform[S](f: Try[T] => Try[S])(implicit executor: ExecutionContext): Future[S]
笔者使用Scala 2.11版本,实现方法如下,读者如果使用Scala2.12+版本可以自行修改后续源码def transForm[T, U](future: Future[T])(f: Try[T] => Try[U]) = { val p = Promise[U] future.onComplete(r => p.tryComplete(f(r))) p.future }
具体实现如下
val promise1 = Future(marry)
val result = Promise[WSResponse]()
val timeout1 = days( 365 * 5 + 1)
val timeout2 = days( 365 * 10 + 2)
val timeout3 = days(365 * 15 + 3)
FutureUtils.keepPromise( (promise1, timeout1), Seq((Future(home), timeout2), (Future(travel(China)), timeout3)))
object FutureUtils {
...
def keepPromise[T](f1: (Future[T], Future[T]), f2: Seq[( => Future[T], => Future[T])]) = {
val result = Promise[T]
transForm(firstCompletedOf(f1._1, f1._2)){
case s @ Success(_) =>
def go(seq: Seq[( => Future[T], Future[T])]): Unit = {
val fl = seq.head._1
transForm(firstCompletedOf(fl, seq.head._2)){
case s @Success(_) =>
go(seq.tail)
s
case f @ Failure(failure) =>
result.failure(failure)
f
}
}
go(f2)
s
case f @ Failure(failure) =>
result.failure(failure)
f
}
result.future
}
}
类似问题还有
明年 有一辆车
从北京周边开始,自驾
一年 走遍河北\河南\山西\山东\辽宁
两年 走遍湖北\湖南\吉林\黑龙江\陕西\宁夏
看似与上文中情况类似,实则却有些许不同.
该问题留作思考.