簡體   English   中英

等待scala中的“遞歸”期貨

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM