2017-03-09 103 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]