Scala Futures 中如何处理异常?

发布于 2024-11-02 02:36:04 字数 671 浏览 1 评论 0原文

我实现了一个简单的作业处理器,用于处理 futures 中的子作业(scala.actors.Futures)。这些 future 本身可以为处理子作业创建更多的 future。现在,如果这些子作业之一引发异常,我希望作业处理器回复该作业的错误消息。我有一个用于发现失败子作业的解决方案,但我不确定这是否是最佳解决方案。基本上它的工作原理是这样的:

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 的列表的列表的递归列表...。我递归地在列表中搜索失败的结果,然后根据结果的类型返回错误或组合结果。 这可行,但我想知道是否有更优雅/更简单的解决方案来处理期货中的异常?

I implemented a simple job processor that processes subjobs within futures (scala.actors.Futures). These futures themselves can create more futures for processing subjobs. Now, if one of these subjobs throws an exception, i want the job processor to reply with an error message for that job. I have a workaround solution for discovering failed subjobs, but i'm not sure if that's the best solution. Basically it works like this:

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

The result at the top level is a recursive List of Lists of Lists... of JobResults. I recursively search the List for a failed Result and then return an error or the combined result depending on the types of results.
That works but i'm wondering if there's is a more elegant/easier solution for dealing with exceptions in futures?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

御守 2024-11-09 02:36:04

您现在所做的方式本质上就是 scala.Either 的设计目的。请参阅 http://www.scala-lang.org/api/current/ scala/Either.html

The way you do it now, is essentially what scala.Either was designed for. See http://www.scala-lang.org/api/current/scala/Either.html

现代 scala future 与 Either 类似,因为它们要么包含成功的结果,要么包含 Throwable。如果你在 scala 2.10 中重新访问这段代码,我想你会发现这种情况非常愉快。

具体来说, scala.concurrent.Future [T] 从技术上讲只有“is-a”Awaitable[T],但是 _.onCompleteAwait.ready(_, timeout)。 value.get 都将其结果显示为 scala.util.Try[T],它很像 Either[Throwable, T] 因为它要么是结果,要么是异常。

奇怪的是,_.transform需要两个映射函数,一个用于T =>; U 和一个 Throwable =>; Throwable 并且(除非我遗漏了一些东西)没有变压器将未来映射为 Try[T] =>尝试[U]Future.map 将允许您通过简单地在映射函数中抛出异常来将成功转变为失败,但它仅将其用于原始 的成功。代码>未来。它的.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

或者你可以“pimp”Future 到具有隐式 def 的 RichFutureWithFlexibleTransform 中,并使 flexibleTransform 成为其中的成员函数,删除 fut 参数并简单地使用 < code>this)

(更好的是采用 Try[T] => Future[U] 并将其命名为 flexibleFlatMap 这样你就可以做异步的事情在变换中)

Modern scala futures are like Either in that they contain either a successful result or a Throwable. If you re-visit this code in scala 2.10, i think you'll find the situation quite pleasant.

Specifically, scala.concurrent.Future[T] technically only "is-a" Awaitable[T], but _.onComplete and Await.ready(_, timeout).value.get both present its result as a scala.util.Try[T], which is a lot like Either[Throwable, T] in that it's either the result or an exception.

Oddly, _.transform takes two mapping functions, one for T => U and one for Throwable => Throwable and (unless i'm missing something) there's no transformer that maps the future as Try[T] => Try[U]. Future's .map will allow you to turn a success into a failure by simply throwing an exception in the mapping function, but it only uses that for successes of the original Future. Its .recover, similarly can turn a failure into a success. If you wanted to be able to change successes to failures and vice-versa, you'd need to build something yourself that was a combination of _.map and _.recover or else use _.onComplete to chain to a new scala.concurrent.Promise[U] like so:

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
}

which would be used like so:

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

which would give something like this:

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

(or you could "pimp" Future into a RichFutureWithFlexibleTransform with an implicit def and make flexibleTransform a member function of that, dropping the fut param and simply using this)

(even better would be to take Try[T] => Future[U] and call it flexibleFlatMap so you could do async things in the transform)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文