在Future的flatMap中抛出异常?

时间:2016-01-28 01:04:02

标签: scala

假设:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

scala>  Future(5).flatMap(_ => throw new Exception("!") )
res9: scala.concurrent.Future[Nothing] = Future(<not completed>)

scala> res9.value
res10: Option[scala.util.Try[Nothing]] = Some(Failure(java.lang.Exception: !))

为什么在Future#flatMap内部抛出异常会返回失败的未来?

鉴于其signature

def flatMap[S](f: (T) ⇒ Future[S])
              (implicit executor: ExecutionContext): Future[S]

_ => throw ...如何返回Future

3 个答案:

答案 0 :(得分:5)

好吧,首先flatMap映射接收未来的结果,所以立即返回另一个未来(因此异常不会立即被捕获!)。传递给flatMap的函数采用了前者未来的结果,必须产生另一个未来。抛出异常的类型为Nothing,这是任何其他类型的子类型,满足Nothing <: Future[A],其中A也被推断为Nothing

从scala.concurrent的角度来看,flatMap(e => (throw new Exception()): Future[A])flatMap(e => Future[A](throw new Exception())必须处理相同,只有一个逻辑解决方案,返回失败的未来。

答案 1 :(得分:1)

throw会返回“底部类型”Nothing http://www.scala-lang.org/api/2.10.4/index.html#scala.Nothing,这是所有其他类型的子类型。因此,在Future中抛出异常会被捕获并返回Future.failed。

  

为什么在Future#flatMap中抛出一个异常会返回一个   失败的未来?

因为它应该如何运作?您还期待什么?

答案 2 :(得分:0)

我不明白@Falmarri说的是什么,恕我直言在Future中抛出一个异常会返回一个失败的未来(这是我期望按照这种方式工作的方式)只是因为异常被捕获(旧学校) java try catch)

考虑Scala 2.12

使用apply方法创建未来时(Future { }Future.apply的语法糖),您实际上是mapping a successful Future

val unit: Future[Unit] = successful(())

def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = unit.map(_ => body)

map是对transform

的调用
def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = transform(_ map f)

transform中的DefaultPromise的{​​{3}}会尝试捕获非致命异常

  override def transform[S](f: Try[T] => Try[S])(implicit executor: ExecutionContext): Future[S] = {
    val p = new DefaultPromise[S]()
    onComplete { result => p.complete(try f(result) catch { case NonFatal(t) => Failure(t) }) } // Here is the magic
    p.future
  }

您可以尝试在onComplete行中添加断点,然后执行

Future { throw new Exception("!") } // or Future.apply(throw new Exception("!")) or Future.unit.map(_ => throw new Exception("!"))

try f(result)将调用Success的map函数传递给你的函数(因为Future.unit的值是Success(())),因为你的函数抛出异常它会被捕获按catch { case NonFatal(t) => Failure(t) }

flatMap发生类似情况,但在这种情况下implementation transformWith

  def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = transformWith {
    case Success(s) => f(s)
    case Failure(_) => this.asInstanceOf[Future[S]]
}

transformWith calls尝试抓住

  override def transformWith[S](f: Try[T] => Future[S])(implicit executor: ExecutionContext): Future[S] = {
    val p = new DefaultPromise[S]()
    onComplete {
      v => try f(v) match {
        case fut if fut eq this => p complete v.asInstanceOf[Try[S]]
        case dp: DefaultPromise[_] => dp.asInstanceOf[DefaultPromise[S]].linkRootOf(p)
        case fut => p completeWith fut
      } catch { case NonFatal(t) => p failure t } // Here is the magic
    }
    p.future
}

但是,我不是scala专家,只是做了一些调试,所以不要把我说的理所当然。