2016-01-21 67 views
2

從官方階documentation一個例子:如果f和g都失敗,如何讓`first`返回`Failure`?

def first[T](f: Future[T], g: Future[T]): Future[T] = { 
    val p = promise[T] 
    f onSuccess { 
    case x => p.trySuccess(x) 
    } 
    g onSuccess { 
    case x => p.trySuccess(x) 
    } 
    p.future 
} 

注意,在本實施方式中,如果既不F,也未克成功,則 第一個(F,G)永遠不會完成(或者與一個值或與 例外)。

它給了我們一個警告,但沒有相應的解決方案。如果f和g都失敗,你怎麼能讓first返回一個Failure

+0

什麼是你想在這裏實現?有兩個期貨並行運行並有第一個回報的解決方案有特別的理由嗎?這引入了代碼執行中的非確定性,並且不能被優雅地解決(也就是說,沒有引入變量來跟蹤狀態)。 –

回答

1

另一種解決方案:你需要找到的第一個成功之間的列表。如果全部失敗,或者失敗。

該解決方案在本質上是一個位effectful,會很高興看到純功能的實現:

def second[T](ls: Seq[Future[T]]): Future[T] = { 
    val p = Promise[T]() 
    val size = ls.size 
    val count = new AtomicInteger(0) 

    ls.foreach(x => x onComplete{ 
    case Success(a) => p.tryCompleteWith(x) 
    case Failure(y) => if(count.incrementAndGet() == size) p.tryCompleteWith(x) 
    }) 
    p.future 
} 

下面的打印2預期:

second(List(Future{ Thread.sleep(5000); 1}, Future{Thread.sleep(3000) ;2}, Future{throw new RuntimeException})) onComplete{ 
    case Success(x) => println(x) 
    case Failure(y) => y.printStackTrace() 
} 
+0

請你詳細說明你的意思:「解決方案本質上有點有效......」 –

+0

@KevinMeredith我的意思是,我維護狀態('count'),然後隨着時間修改它的狀態。這是一個副作用。理想情況下,我希望避免它的方式。 – Jatin

3

像這樣的事情可以做的工作,雖然它不是基於同樣的邏輯,你的榜樣(這個解決方案是錯誤,檢查更新

def first[T](f: Future[T], g: Future[T]): Future[T] = { 
    val p = Promise[T] 
    p.completeWith(f.fallbackTo(g)) 
    p.future 
} 

如果同時出現故障,firstholds來自f的可拋出物。

更新(基於@ Jatin的評論): 我的第一個解決方案是錯誤的,如果f未來無法完成,因爲,那麼即使g確實,first永遠不會完成任何。此更新的解決方案完成了Promise不確定地與第一完成值:

def first[T](f: Future[T], g: Future[T]): Future[T] = { 
    val p = Promise[T] 
    p.completeWith(Future.firstCompletedOf(Seq(f.fallbackTo(g),g.fallbackTo(f)))) 
    p.future 
} 
+0

它有一個缺陷。如果'g'快速完成並且'f'需要更長的時間,那麼返回的將來永遠不會完成 – Jatin

+0

嗯,我沒有看到你的觀點,對不起。你能詳細說明一下嗎? 'Future {Thread.sleep(1); 5},Future [Int] {1})'似乎工作得很好 – kosii

+1

用'first(Future {Thread.sleep(100000); 5},Future [Int ] {1})'。它會永遠等待 – Jatin

相關問題