在for comprehension中抛出异常时的行为不一致

时间:2016-10-15 08:01:06

标签: scala

我注意到 for comprehension 在其内部的第一行与任何其他行抛出异常时行为不一致。

请考虑以下示例代码:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Success}

object ForComprehensionTester {
  def main(args: Array[String]): Unit = {
    val result: Future[String] = for {
      x <- returnString() // Comment out this line and exception will not be captured, but propagated.
      y <- throwException()
    } yield y

    result.onComplete {
      case Success(s) => System.out.println(s"Success: $s")
      case Failure(t) => System.out.println(s"Exception captured!")
    }

    Thread.sleep(2000)
  }

  def returnString() = Future {
    "content"
  }

  def throwException(): Future[String] = throw new RuntimeException
}

上述代码导致在 onComplete 函数中捕获和处理异常。但是如果我们注释掉第8行,那么异常将被传播。

有人可以解释发生了什么吗?

1 个答案:

答案 0 :(得分:1)

理解是语法糖。如果您根据mapflatMap

进行思考,就可以了解行为
val result: Future[String] = for {
      x <- returnString() // Comment out this line and exception will not be captured, but propagated.
      y <- throwException()
    } yield y

上面的代码转换为

returnString.flatMap { str =>
  throwException()
}

以下是标准库中的flatMap实现。 flatMap处理其中抛出的异常。

def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = {
    import impl.Promise.DefaultPromise
    val p = new DefaultPromise[S]()
    onComplete {
      case f: Failure[_] => p complete f.asInstanceOf[Failure[S]]
      case Success(v) => try f(v) match {
        // If possible, link DefaultPromises to avoid space leaks
        case dp: DefaultPromise[_] => dp.asInstanceOf[DefaultPromise[S]].linkRootOf(p)
        case fut => fut.onComplete(p.complete)(internalExecutor)
      } catch { case NonFatal(t) => p failure t }
    }
    p.future
  }

当你注释掉returnString进行理解时

throwException() 

请注意,throwException抛出的异常不在函数中处理。

如下所示声明throwException以捕获异常

def throwException(): Future[String] = Future { throw new RuntimeException }
相关问题