[英]waiting for “recursive” futures in scala
一個描述我的問題的簡單代碼示例:
import scala.util._
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
class LoserException(msg: String, dice: Int) extends Exception(msg) { def diceRoll: Int = dice }
def aPlayThatMayFail: Future[Int] = {
Thread.sleep(1000) //throwing a dice takes some time...
//throw a dice:
(1 + Random.nextInt(6)) match {
case 6 => Future.successful(6) //I win!
case i: Int => Future.failed(new LoserException("I did not get 6...", i))
}
}
def win(prefix: String): String = {
val futureGameLog = aPlayThatMayFail
futureGameLog.onComplete(t => t match {
case Success(diceRoll) => "%s, and finally, I won! I rolled %d !!!".format(prefix, diceRoll)
case Failure(e) => e match {
case ex: LoserException => win("%s, and then i got %d".format(prefix, ex.diceRoll))
case _: Throwable => "%s, and then somebody cheated!!!".format(prefix)
}
})
"I want to do something like futureGameLog.waitForRecursiveResult, using Await.result or something like that..."
}
win("I started playing the dice")
這個簡單的例子說明了我想做的事情。 基本上,如果用文字表達,我想等待一些計算的結果,當我對先前的成功或失敗的成功構成不同的行動。
那你怎么實現win
方法呢?
我的“真實世界”問題,如果它有任何區別,就是使用dispatch
進行異步http調用,我想在前一個調用結束時繼續進行http調用,但是前一個http調用成功與否的操作不同。
您可以通過遞歸調用恢復失敗的未來:
def foo(x: Int) = x match {
case 10 => Future.successful(x)
case _ => Future.failed[Int](new Exception)
}
def bar(x: Int): Future[Int] = {
foo(x) recoverWith { case _ => bar(x+1) }
}
scala> bar(0)
res0: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@64d6601
scala> res0.value
res1: Option[scala.util.Try[Int]] = Some(Success(10))
recoverWith
采用PartialFunction[Throwable,scala.concurrent.Future[A]]
並返回Future[A]
。 你應該小心,因為它會在這里進行大量的遞歸調用時使用相當多的內存。
由於drexin回答了關於異常處理和恢復的部分,讓我嘗試回答有關涉及期貨的遞歸函數的部分。 我相信使用Promise
將幫助您實現目標。 重組后的代碼如下所示:
def win(prefix: String): String = {
val prom = Promise[String]()
def doWin(p:String) {
val futureGameLog = aPlayThatMayFail
futureGameLog.onComplete(t => t match {
case Success(diceRoll) => prom.success("%s, and finally, I won! I rolled %d !!!".format(prefix, diceRoll))
case Failure(e) => e match {
case ex: LoserException => doWin("%s, and then i got %d".format(prefix, ex.diceRoll))
case other => prom.failure(new Exception("%s, and then somebody cheated!!!".format(prefix)))
}
})
}
doWin(prefix)
Await.result(prom.future, someTimeout)
}
現在這不會是真正的遞歸,因為它將構建一個長堆棧,因為期貨是異步的,但它類似於精神中的遞歸。 使用promise在這里給你一些東西來阻止,而遞歸就是這樣,阻止調用者從場景背后發生的事情。
現在,如果我這樣做,我很可能會重新定義這樣的事情:
def win(prefix: String): Future[String] = {
val prom = Promise[String]()
def doWin(p:String) {
val futureGameLog = aPlayThatMayFail
futureGameLog.onComplete(t => t match {
case Success(diceRoll) => prom.success("%s, and finally, I won! I rolled %d !!!".format(prefix, diceRoll))
case Failure(e) => e match {
case ex: LoserException => doWin("%s, and then i got %d".format(prefix, ex.diceRoll))
case other => prom.failure(new Exception("%s, and then somebody cheated!!!".format(prefix)))
}
})
}
doWin(prefix)
prom.future
}
這樣,您可以推遲決定是否阻止或使用此函數調用者的異步回調。 這樣更靈活,但它也使調用者暴露於您正在進行異步計算的事實,並且我不確定您的方案是否可以接受。 我會把這個決定留給你。
這對我有用:
def retryWithFuture[T](f: => Future[T],retries:Int, delay:FiniteDuration) (implicit ec: ExecutionContext, s: Scheduler): Future[T] ={
f.recoverWith { case _ if retries > 0 => after[T](delay,s)(retryWithFuture[T]( f , retries - 1 , delay)) }
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.