我实现了一个简单的作业处理器,用于处理期货中的子作业(scala.actors.Futures)。这些期货本身可以为加工子工作创造更多的未来。现在,如果其中一个子工具抛出异常,我希望作业处理器回复该作业的错误消息。我有一个解决方法来发现失败的subjobs,但我不确定这是否是最好的解决方案。基本上它的工作原理如下:
sealed trait JobResult
case class SuccessResult(content: String) extends JobResult
case class FailedResult(message: String) extends JobResult
for(subjob <- subjobs) yield {
future {
try {
SuccessResult(process(subjob))
} catch {
case e:Exception => FailedResult(e.getMessage)
}
}
}
顶级的结果是JobResults的递归列表列表....我递归搜索List以查找失败的结果,然后根据结果类型返回错误或组合结果。 这是有效的,但我想知道是否有一个更优雅/更容易的解决方案来处理期货中的例外?
答案 0 :(得分:5)
你现在的方式,实质上就是scala.Either的设计目的。见http://www.scala-lang.org/api/current/scala/Either.html
答案 1 :(得分:2)
现代scala期货与Either
类似,因为它们包含成功结果或Throwable
。如果您在scala 2.10中重新访问此代码,我认为您会发现情况非常愉快。
具体来说,scala.concurrent.Future[T]技术上只是“is-a”Awaitable[T]
,但_.onComplete
和Await.ready(_, timeout).value.get
都将其结果显示为scala.util.Try[T],这是一个很像Either[Throwable, T]
,因为它是结果或异常。
奇怪的是,_.transform
有两个映射函数,一个用于T => U
,一个用于Throwable => Throwable
和(除非我遗漏了一些东西)没有变换器将未来映射为{{1 }}。 Try[T] => Try[U]
的{{1}}将允许您通过在映射函数中抛出异常来将成功转化为失败,但它仅将其用于原始Future
的成功。其.map
同样可以将失败转化为成功。如果您希望能够将成功更改为失败,反之亦然,那么您需要自己构建一些由Future
和.recover
组合的内容,或者使用_.map
来构建链接到这样的新scala.concurrent.Promise[U]:
_.recover
会像这样使用:
_.onComplete
会产生这样的结果:
import scala.util.{Try, Success, Failure}
import scala.concurrent.{Future, Promise}
import scala.concurrent.ExecutionContext
def flexibleTransform[T,U](fut: Future[T])(f: Try[T] => Try[U])(implicit ec: ExecutionContext): Future[U] = {
val p = Promise[U]
fut.onComplete { res =>
val transformed = f(res)
p.complete(transformed)
}
p.future
}
(或者你可以将import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration.Duration.Inf
def doIt() {
val a: Future[Integer] = Future {
val r = scala.util.Random.nextInt
if (r % 2 == 0) {
throw new Exception("we don't like even numbers")
} else if (r % 3 == 0) {
throw new Exception("we don't like multiples of three")
} else {
r
}
}
val b: Future[String] = flexibleTransform(a) {
case Success(i) =>
if (i < 0) {
// turn negative successes into failures
Failure(new Exception("we don't like negative numbers"))
} else {
Success(i.toString)
}
case Failure(ex) =>
if (ex.getMessage.contains("three")) {
// nevermind about multiples of three being a problem; just make them all a word.
Success("three")
} else {
Failure(ex)
}
}
val msg = try {
"success: " + Await.result(b, Inf)
} catch {
case t: Throwable =>
"failure: " + t
}
println(msg)
}
for { _ <- 1 to 10 } doIt()
“pimp”failure: java.lang.Exception: we don't like even numbers
failure: java.lang.Exception: we don't like negative numbers
failure: java.lang.Exception: we don't like negative numbers
success: three
success: 1756800103
failure: java.lang.Exception: we don't like even numbers
success: 1869926843
success: three
failure: java.lang.Exception: we don't like even numbers
success: three
加入隐含def的Future
,并使RichFutureWithFlexibleTransform
成为其成员函数,放弃flexibleTransform
参数并简单地使用fut
)
(更好的方法是选择this
并将其称为Try[T] => Future[U]
,这样您就可以在变换中执行异步操作)