假设:
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
?
答案 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专家,只是做了一些调试,所以不要把我说的理所当然。