关于scala,如何等待Future.onFailure完成?

时间:2016-09-14 16:32:57

标签: scala

斯卡拉期货: 使用Await.result,我可以在终止程序之前等待将来完成。

如何在终止程序之前等待Future.onFailure完成?

package test

import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global

object Test5 extends App {
  def step(i: Int): Future[String] = Future {
    Thread.sleep(1000)
    if (i == 3) throw new Exception("exception on t3")
    Thread.sleep(1000); val s = s"done: $i"; println(s); s
  }

  val task: Future[String] = step(1).flatMap(_ => step(2).flatMap(_ => step(3)))

  task.onFailure {
    case e: Throwable => println("task.onFailure. START: " + e); Thread.sleep(3000); println("task.onFailure. END.")
  }

  try {
    val result: String = Await.result(task, Duration.Inf)
    println("RESULT: " + result)
  } catch {
    case e: Throwable => println("Await.result.try catch: " + e)
  }

  // without this, "task.onFailure. END." does not get printed.
  // How can I replace this line with something such as Await.result([the task.onFailure I set previously])
  Thread.sleep(5000)  

  println("END")
}

注意:我可以在task.onFailure上捕获异常(如示例中所示),而不是使用Await.result。但我更愿意使用task.onFailure

更新 解决方案是使用Ryan提出的transformrecover。在我的情况下,我需要更多的东西,并且我已经添加了这个替代答案:on scala, how to wait for a Future.onFailure to complete?

4 个答案:

答案 0 :(得分:1)

onFailure返回Unit。没有办法阻止它。

捕获Await.result上的例外或使用transformrecover对例外产生副作用。

答案 1 :(得分:1)

如果您只想对副作用进行排序,以便知道它已被执行,我建议使用'andThen'。

答案 2 :(得分:0)

你做不到。 Future#onFailure返回Unit,因此没有句柄可以阻止执行。

由于您阻止完成task ,代码中的catch子句与您从onFailure获得的内容相同。也就是说,如果Future失败,catch的主体将在程序退出之前执行,因为它在主线程上运行(而onComplete不是)。

答案 3 :(得分:0)

我们可以使用FutureonComplete2丰富onCompleteWith2,如下所示:

import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.util.Try

object FutureUtils {
  implicit class RichFuture[T](val self: Future[T]) {
    def onComplete2[U](f: (Try[T]) => U)(implicit executor: ExecutionContext): Future[T] = {
      val p = Promise[T]()
      self.onComplete { r: Try[T] => f(r); p.complete(r) }
      p.future
    }

    def onCompleteWith2[U](f: (Try[T]) => Future[U])(implicit executor: ExecutionContext): Future[T] = {
      val p = Promise[T]()
      self.onComplete { r: Try[T] => f(r).onComplete(_ => p.complete(r)) }
      p.future
    }    
  }
}

然后我的情况如下使用:

import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import FutureUtils._

import scala.util.{Failure, Success, Try}

object Test5 extends App {
  def stepf(i: Int): Future[String] = Future { step(i) }

  def step(i: Int): String = {
    Thread.sleep(1000)
    if (i == 3) throw new Exception("exception on t3")
    Thread.sleep(1000); val s = s"done: $i"; println(s); s
  }

  val task1: Future[String] =
    stepf(1).flatMap(_ => stepf(2).flatMap(_ => stepf(3)))

  // the result of step(10) and step(11) is ignored
  val task2: Future[String] = task1.onComplete2((r: Try[String]) => r match {
    case Success(s) => step(10)
    case Failure(e) => step(11)
  })

/*
  // If I want to recover (and so, to avoid an exception on Await.result:
  val task3 = task2.recover {case e: Throwable => step(12) }


  // I can use onCompleteWith2 instead of onComplete2
  val task2: Future[String] = task1.onCompleteWith2((r: Try[String]) => r match {
    case Success(s) => stepf(10)
    case Failure(e) => stepf(11)
  })
*/

  try {
    val result = Await.result(task2, Duration.Inf)
    println("RESULT: " + result)
  } catch {
    // see my comment above to remove this
    case e: Throwable => println("Await.result.try catch: " + e)
  }

  println("END.")
}

执行如下:

done: 1
done: 2
done: 11
Await.result.try catch: java.lang.Exception: exception on t3
END.

如果我们不在step(3)处抛出异常,则执行如下。请注意,结果是"完成:3",不是"完成:10":

done: 1
done: 2
done: 3
done: 10
RESULT: done: 3
END.