2016-10-21 107 views
3

我正在scala中編寫一個遞歸重試函數,我想知道未來創建時是否存在運行時錯誤。如果有,那麼未來的實例化將被重試。未來返回類型的Scala遞歸函數

想象我有一個數據庫查詢功能:

dbLookup(userId : UserId) : Option[UserName] = ??? 

重試看起來形式的東西:

retry[T](f :() => Future[Option[T]], 
     notifyFailure : (t : Throwable) => Unit, 
     n : Int) : Future[Option[T]] = { 
    if(n <= 0) { Future{None} } 
    else { 
    val fut = f() 
    if(f.resultsInException) {  //I don't know how to write this 
     notifyFailure(f.exception) 
     retry(f, notifyFailure, n-1) //try again 
    } 
    else { 
     f.originalValueAsFuture 
    } 
    } 
} 

怎麼可以這樣將來的功能來實現,並允許尾遞歸優化?

該功能可用於重試數據庫10次爲一個用戶,如果執行上下文不斷拋出一個異常時,我嘗試創建一個未來:

val userNameAfterRetries = 
    retry(() => Future{dbLookup("1234")}, 
     (t) => system error (s"future creation error : $t"), 
     10) 

注:這是有點可能的Future.fallbackTo,但不幸fallbackTo需要Future[T]而不是() => Future[T]。這很重要,因爲即使第一次嘗試成功,使用fallbackTo也會導致至少再次嘗試1次。

預先感謝您的考慮和迴應。

+0

你可以使用未來的「onFailure」嗎?也許我錯過了什麼? – Jegan

+0

@Jegan'onFailure'可以「註冊」notifyFailure運行,如果失敗ocurrs,但然後我需要返回一個重試的值... –

回答

4

這個怎麼樣?

def retry[T](f:() => Future[Option[T]], 
       notifyFailure: Throwable => Unit, 
       n: Int)(implicit ec : ExecutionContext): Future[Option[T]] = { 
    if (n <= 0) Future.failed(new RuntimeException("Exceeded number of allowed retries")) 
    else f().recoverWith { case originalError => notifyFailure(originalError); retry(f, notifyFailure, n - 1) } 
    } 

上尾遞歸更新:的Future本質是異步的,除非你想await的結果,我不太看它是可能使它@tailrec因爲你將不得不在回調使用遞歸。

另外實用說明:如果你知道它總是~10次重試,我不會害怕遞歸。

+0

更新:其實最簡單的解決方案最小的一個可能:) – tkachuko

+0

我想,該問題要求提供尾遞歸解決方案。 – Jegan

+0

實際上,看看「else」部分,它用遞歸調用「retry」方法恢復未來 – tkachuko