斯卡拉风格与期货

时间:2017-03-09 05:37:08

标签: scala asynchronous

寻找编写需要一个接一个地运行异步的函数链的最佳方法。鉴于这两个选项:

选项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. 这有什么好的做法吗?
  4. 另一个问题,鉴于此功能:

    def foo: Future[A]
    

    如果我想将结果转换为单位,这是最好的方法:

    foo map { _ => () } 
    

    谢谢!

1 个答案:

答案 0 :(得分:3)

选项1 优于选项2 的潜在优势在于,它保证operation2将在operation1后立即运行 - 如果它没有&# 39; t因异常而失败 - 而在选项2 中,当flatMap完成时,您可能已经耗尽了线程池可用线程。

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

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

您必须声明一个隐式执行上下文,或者导入它:它确定您正在使用哪个池。如果您导入了默认的global执行程序,那么您的池是一个基于fork的连接,默认情况下 - 与您加工的内核一样多的线程。

第一个选项就像让一个线程同时运行两个操作,而第二个选项在一个线程中运行第一个操作,然后尝试从ExecutionContext获取另一个线程来运行第二个操作。 /强>

最佳做法是使用您需要的东西:

您是否要确保operation2在执行上下文中没有更多线程可用的上下文中运行?如果答案为是,则使用 Option1 < / strong>即可。否则,您可以使用 Option2

关于您的上一个问题:您在提议的代码段中执行的操作不是强制转换,您正在映射一个函数,该函数为Unit类型的任何值提供A值。结果是你得到了Unit类型的未来,这对于检查其完成状态很有用。这是获得你想要的东西的最好方法。

然而,请注意,与flatMap一样,执行该&#34;转换功能&#34;将在您的上下文中的隐式执行程序提供的不同线程中运行。这就是map也有隐含参数executor的原因:

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