【Scala笔记——道】给你的Future一个Promise --最浪漫的并发模型(二)

这篇一直想不到如何动笔,这段时间刚好做了一些性能优化的工作,而后还有更多.遂提笔权当记录.

迟到的Promise

“五年,凭此戒娶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!"))异常.

更多的Promise

十年
要带你去看周杰伦的演唱会
要有我们的家
要带你走遍中国
要…

热恋的人们其实有一种默契,对于一些头脑发热时说的话,一首曲子在尾音还没有落下时便已消散.
但程序员的浪漫从来不纠结于此,那毫秒级的精确,才是属于程序员浪漫的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(...)) ]

更复杂的Promise

比起头脑发热的胡话,更可怕的是清醒的时候说胡话.信誓旦旦为自己的愚蠢列出一些列详细的计划,在真正用代码记录这种带有着强烈实际规划的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
    }
}

类似问题还有

明年 有一辆车
从北京周边开始,自驾
一年 走遍河北\河南\山西\山东\辽宁
两年 走遍湖北\湖南\吉林\黑龙江\陕西\宁夏

看似与上文中情况类似,实则却有些许不同.

  1. 第一个Future(有一辆车) 是从明年开始记,超时实际是一年
  2. 但环游中国是从买了第一辆车开始开始记.也就是说,如果车是在半年后买,第一旅行计划实现实际限定时间在一年半后,第二旅行计划在两年半.

该问题留作思考.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值