如何在终止程序之前等待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提出的transform
或recover
。在我的情况下,我需要更多的东西,并且我已经添加了这个替代答案:on scala, how to wait for a Future.onFailure to complete?
答案 0 :(得分:1)
onFailure
返回Unit
。没有办法阻止它。
捕获Await.result
上的例外或使用transform
或recover
对例外产生副作用。
答案 1 :(得分:1)
如果您只想对副作用进行排序,以便知道它已被执行,我建议使用'andThen'。
答案 2 :(得分:0)
你做不到。 Future#onFailure
返回Unit
,因此没有句柄可以阻止执行。
由于您阻止完成task
,代码中的catch
子句与您从onFailure
获得的内容相同。也就是说,如果Future
失败,catch
的主体将在程序退出之前执行,因为它在主线程上运行(而onComplete
不是)。
答案 3 :(得分:0)
我们可以使用Future
和onComplete2
丰富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.