这篇一直想不到如何动笔,这段时间刚好做了一些性能优化的工作,而后还有更多.遂提笔权当记录.
“五年,凭此戒娶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 } }类似问题还有
明年 有一辆车 从北京周边开始,自驾 一年 走遍河北\河南\山西\山东\辽宁 两年 走遍湖北\湖南\吉林\黑龙江\陕西\宁夏
看似与上文中情况类似,实则却有些许不同.
第一个Future(有一辆车) 是从明年开始记,超时实际是一年但环游中国是从买了第一辆车开始开始记.也就是说,如果车是在半年后买,第一旅行计划实现实际限定时间在一年半后,第二旅行计划在两年半.该问题留作思考.