Scala基于for-comprehension按条件执行Future任务方案咨询
实现方案说明
首先避坑:你想到的直接外层加if的写法不可用
你写的外层独立if判断有两个严重问题:
- 不会等待exec1~exec3执行完成就会触发exec4、exec5的执行,打破了原有的串行执行依赖逻辑
- exec4、exec5的执行状态、异常都不会被
execAll返回的Future捕获,相当于异步任务失控,会出现任务丢失、异常无感知的问题,绝对不要这么用。
方案1:直接在原有for-comprehension中添加判断(最简洁)
利用Future.successful(())作为不满足条件时的占位符,不改变原有串行逻辑,可读性最高:
import scala.concurrent.Future def execAll(country: String): Future[Unit] = { val needExecExtra = Set("en", "es").contains(country) for { _ <- repo.exec1(country) _ <- repo.exec2(country) _ <- repo.exec3(country) _ <- if (needExecExtra) repo.exec4(country) else Future.successful(()) _ <- if (needExecExtra) repo.exec5(country) else Future.successful(()) } yield () }
方案2:拆分公共/专属任务后拼接(适合多任务绑定的场景)
你拆分两个Future链的思路是可行的,只是拼接不能用+,Future串行拼接用flatMap即可,注意要避免Future提前执行的坑:
Scala的Future是创建即执行的,如果把专属任务定义成公共val,即使国家是de,exec4、exec5也会偷偷执行,只是你没等它完成而已。正确写法如下:
def execAll(country: String): Future[Unit] = { // 公共任务,所有国家都会执行 val commonTask = for { _ <- repo.exec1(country) _ <- repo.exec2(country) _ <- repo.exec3(country) } yield () country match { case "de" => commonTask case _ => // 专属任务只在非de的场景下创建,避免提前执行 commonTask.flatMap(_ => for { _ <- repo.exec4(country) _ <- repo.exec5(country) } yield () ) } }
方案3:Future列表方式(不推荐)
如果要用Future列表实现,要注意Future.sequence是并行执行所有任务,不符合你串行的需求。要串行的话需要用foldLeft手动拼接,代码冗余度高,不如前两种方案简洁。
方案选择建议
- 如果exec4、exec5永远是绑定执行/不执行的关系,选方案2,代码更干净,没有重复判断
- 如果后续可能调整为exec4、exec5各自有不同的执行条件,选方案1,灵活性更高
内容的提问来源于stack exchange,提问作者AlleXyS
相关产品推荐
相关产品推荐

