2017-03-09 106 views
1

寻找最好的方式来编写需要一个接一个地运行异步的函数链。鉴于这两个选项:带期货的斯卡拉风格

选项1

def operation1(): Unit = {...} 
def operation2(): Unit = {...} 

def foo(): Future[Unit] = 
    Future { 
    operation1() 
    operation2() 
    } onComplete {case _ => println("done!")} 

选项2

def operation1(): Future[Unit] = {...} 
def operation2(): Future[Unit] = {...} 

def foo(): Future[Unit] = { 
    operation1() 
    .flatMap {case _ => operation2() } 
    .onComplete {case _ => println("done!")} 
} 
  1. 是否有一个比其他任何优点/缺点?
  2. 我相信选项1将在同一后台线程上运行这两个函数。选项2也是这种情况吗?
  3. 这有什么好的做法吗?

另一个问题,因为这个功能:

def foo: Future[A] 

如果我想要的结果转换为单位,这是做到这一点的最好办法:

foo map { _ =>() } 

谢谢!

回答

3

选项1选项2的潜在优势是,它保证operation2operation1后立即运行 - 如果它不具有异常失败 - 而在选项2,你可能有当flatMap要完成时,用尽线程池可用线程。

是的,选项1肯定会在同一个线程中运行操作。 选项2将尝试在两个线程中运行它们,只要它们有足够的可用空间。

flatMap[S](f: (T) ⇒ Future[S])(implicit executor: ExecutionContext): Future[S] 

您确实必须声明隐式执行上下文或导入它:它确定您正在使用哪个池。如果你导入了默认的global执行程序,那么你的池是一个fork连接,基于默认情况下与你机器核心具有的线程数相同。

第一个选项就像让一个线程一个接一个地运行两个操作,而另一个选项运行线程中的第一个操作,然后尝试从ExecutionContext获取另一个线程来运行第二个操作。

最好的做法是使用你所需要的:

你想确保operation2运行的情况下在没有更多的线程在执行方面有哪些?如果答案是肯定的,则使用Option1。否则,你可以使用选项2

关于你的最后一个问题:你做对你提出的片段是什么不投,你正在映射这为A类型的任何值的Unit值的函数。其效果是您得到了类型Unit的未来,这对于检查其完成状态很有用。这是获得你想要的最好方式。

但请注意,除了flatMap之外,该“转换函数”的执行将在由上下文中的隐式执行程序提供的不同线程中运行。这就是为什么map也有一个隐含参数executor

def map[S](f: (T) ⇒ S)(implicit executor: ExecutionContext): Future[S]