2011-04-19 85 views
3

我实现了一个简单的作业处理器,处理期货(scala.actors.Futures)中的subjobs。这些期货本身可以为处理subjobs创造更多的期货。现在,如果其中一个子作业引发异常,我希望作业处理器回复该作业的错误消息。我有一个解决方案来发现失败的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。我递归搜索列表失败的结果,然后返回一个错误或组合的结果取决于结果的类型。 这有效,但我想知道是否有一个更优雅/更容易的解决方案来处理期货的例外情况?

回答

2

现代斯卡拉期货是在Either它们包含一个成功的结果或Throwable。如果你重新访问scala 2.10中的这段代码,我想你会发现情况相当愉快。

具体地,技术上scala.concurrent.Future[T]仅“是一个” Awaitable[T],但_.onCompleteAwait.ready(_, timeout).value.get都呈现其结果作为scala.util.Try[T],这是很多像在于它要么结果或异常Either[Throwable, T]

奇怪的是,_.transform需要两个映射函数,一个用于T => U,一个用于Throwable => Throwable和(除非我失去了一些东西),有没有变压器,未来映射为Try[T] => Try[U]Future.map将允许您通过简单地在映射函数中抛出异常来将成功转化为失败,但它仅使用原始Future的成功。它的.recover,同样可以将失败转化为成功。如果你想成为能够改变对成功失败,反之亦然,你需要建立自己的东西,这是的_.map_.recover或者组合使用_.onComplete到链到一个新的scala.concurrent.Promise[U]像这样:

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() 

这将使这样的事情:

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 

(或者你可以“皮条客” FutureRichFutureWithFlexibleTransform有一个隐含的清晰度,使flexibleTransform的一个成员函数,丢弃fut PARAM和简单的使用this

(甚至更好的是采取Try[T] => Future[U]并调用它flexibleFlatMap所以你可以做转换异步的东西)

+1

你可能想提一下这个类型:['Try'](http://www.scala-lang.org/files/archive/nightly/docs/library/index的.html#scala.util.Try) – bluenote10 2014-05-01 15:27:10