【问题标题】:Why does Future.onSuccess not fail the future?为什么 Future.onSuccess 不会让未来失败?
【发布时间】:2016-11-07 18:51:04
【问题描述】:

假设,我想在未来完成后对结果进行一些验证:

 def futureUser() {
   val foo = Future(getUserData(userId))
   foo.onSuccess { 
     case None => throw new InvalidUserId(userId)
   }
   foo // This sucks too btw: why doesn't `onSuccess` return `this` ???
 } 

这根本不像它看起来的那样:futureUser 返回一个成功的FutureNone,异常被转储到控制台并“swallen”。 看起来这实际上是故意完成的(推特期货的行为方式相同......至少,.onSuccess 是“可链接的”)。为什么?这是什么原因?

更新 好的,我需要提一下,这里使用Option 只是为了举例。请考虑一下:

def futureItems() = Future(getInterestingItems()).map { 
    case Nil => throw new NothingInterestingFound()
    case bar => bar // <-- this is the ugly piece that I could avoid if .onSuccess did not swallow failures
  }

【问题讨论】:

  • 这就是 API 的设计方式。 onSuccess 适合那些想在回调和副作用方面思考/工作的人。如果你想要好的组合组合器,那么也有很多。
  • @TravisBrown 好吧,这就是问题所在,除了非常丑陋的foo.map { case None =&gt; throw ... ; case x =&gt; x } 之外,实际上似乎没有任何“漂亮的组合器”可用于我的用例:(我错过了吗?
  • @TravisBrown @Dima 不是 ScalaZ 的 Task 表现得像你想要的那样吗?
  • @Dima 为什么不只是flatMapFuture.failed?一般来说,在这种情况下,无论如何,我都想在结果中去掉Option
  • @TravisBrown,Option 仅用于示例。在现实生活中还有其他可能性。例如List(请参阅我更新的问题)。

标签: scala future


【解决方案1】:

这样写怎么样:

foo map { _.getOrElse(throw new Exception()) }

如 cmets 所述,onSuccess 仅在您不关心结果时才有用(只有副作用很重要)。

例如,您可以登录onSuccess。发送电子邮件也很有意义(您想在事情成功时发送它,但您不需要等待发送完成)。

【讨论】:

  • 适用于Option,但其他结果类型呢?例如,如果List 为空,则抛出? (查看更新的问题),
  • 我认为filter 应该可以解决问题:foo filter { _.nonEmpty }。但是,如果列表为空,则会抛出NoSuchElementException,参见scala-lang.org/api/2.9.3/scala/concurrent/Future.html
  • 完全正确。我需要返回一个特定于应用程序的异常,描述错误情况。您对我提供的示例阅读过多。实际的现实生活中的验证并不像这样原始。
  • 怎么样:foo filter { _.nonEmpty } recover { throw new AppSpecificException() }。不确定它是否比您的 map 更优雅,但...
【解决方案2】:

如果您只想对成功的Future 进行验证,那么您只需要transform

Future(getInterestingItems()).transform(validate, identity)

甚至可以很好地加糖:

implicit class FutureFailureTransform[T](private val f: Future[T]) extends AnyVal {
  def validated(isValid: T => T): Future[T] = {
    f.transform(isValid, identity)
  }
}

// Use
Future(getInterestingItems()).validated {
  case Nil => throw new NothingInterestingFound()
}

或者,如果您对NoSuchElementException 没问题,您可以使用Future.collect

Future(getInterestingItems()) collect {
  case InterestingCase(data) => // Do stuff here
}

如果当前的未来包含一个定义了偏函数的值,那么新的未来也将持有该值。否则,生成的 future 将失败并返回 NoSuchElementException

如果当前的未来失败了,那么由此产生的未来也失败了。

如果您需要不同的例外,您只需将collecttransform 链接起来:

transform(identity, _ => new NothingInterestingFound())

【讨论】:

  • OP 似乎想要不匹配的值的标识,这与 collect 所做的相反。
  • 如果你想要一个不同的例外,只需使用transform(identity, YourFailureConstructor(_)) ...对吗?
  • @TravisBrown 啊,没错,我错过了。 .filter,就像其他答案所暗示的那样可行,但我会被 NoSuchElementException 卡住,并带有一个神秘的(从下游用户的角度)消息“谓词不满足”......
  • @SeanVieira transform 有效,但这看起来比我使用.map 的解决方案更难看:)
  • @Dima - 更新了(希望)更漂亮的解决方案。
猜你喜欢
  • 1970-01-01
  • 2016-04-11
  • 2021-10-10
  • 2013-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-17
相关资源
最近更新 更多