【问题标题】:Failing a Future based on a successful result基于成功的结果失败的未来
【发布时间】:2019-03-14 02:57:01
【问题描述】:

我的代码中有一个场景,我需要根据包含特定值的成功结果使Future 失败。我可以通过flatMap 完成这项工作,但我想知道是否有更清洁的方法来完成这项工作。首先,一个非常简化的例子:

import concurrent._

case class Result(successful:Boolean)
object FutureTest {
  def main(args: Array[String]) {
    import ExecutionContext.Implicits._

    val f = Future{Result(false)}.flatMap{ result =>
      result match{
        case Result(false) => Promise.failed(new Exception("The call failed!!")).future
        case _ => Promise.successful(result).future
      }
    }

    f onFailure{
      case x => println(x.getMessage())
    }
  }
}

因此,在我的示例中,如果返回的 Result 的成功指标值为 false,我希望 Future 失败。正如我所提到的,我可以使用flatMap 使这项工作正常,但我想删除的代码行是:

case _ => Promise.successful(result).future

这句话似乎没有必要。我想要的行为是能够定义条件,如果它评估为真,允许我返回一个不同的Future,但如果它不是真的,就保持原样(有点比如 PartialFunction 语义。有没有办法做到这一点,我只是没有看到?我看过 collecttransform ,它们似乎也不合适。

编辑

在得到@Rex Kerr 和@senia 的map 建议后,我创建了一个PimpedFuture 和一个隐式转换来美化代码:

class PimpedFuture[T](f:Future[T])(implicit ex:ExecutionContext){
  def failWhen(pf:PartialFunction[T,Throwable]):Future[T] = {
    f map{
      case x if (pf.isDefinedAt(x)) => throw pf(x)
      case x => x
    }
  }
}

隐含的

  implicit def futToPimpedFut[T](fut:Future[T])(implicit ec:ExecutionContext):PimpedFuture[T] = new PimpedFuture(fut)

以及新的处理代码:

val f = Future{ Result(false) } failWhen {
  case Result(false) => new Exception("The call failed!!")
}

我认为这更简洁一些,同时仍然使用map 的建议。

【问题讨论】:

  • 您希望在这两种情况下都有 new 未来?还是只在失败的情况下?
  • 结果本身,在我的实际代码中,是从对演员的调用中返回的。鉴于这种结果,我不希望那个演员总是在上游传播失败。它基于通过调用添加键成功地将值存储到沙发库中。不是所有的调用代码都关心成功标志的值是否为假;这是情境性的。
  • 听起来像filter 方法。
  • @om-nom-nom,如果满足我的匹配条件,我只想要一个新的未来。如果不满足匹配,则不理会现有的未来。
  • @senia,我看了filter。那里的问题是,如果我的谓词不正确,那么生成的 Future 将失败,NoSuchElementException 是不希望的。

标签: scala future


【解决方案1】:

您可以通过map 轻松做到这一点:

val f = Future{ Result(false) } map {
  case Result(false) => throw new Exception("The call failed!!")
  case x => x
}

但您仍然需要明确提及您正在传递身份。

请注意,您 should not 在这种情况下使用 andThenandThen 是为了产生副作用,而不是为了改变结果(不像Function1 的同名方法)。

【讨论】:

  • 你应该使用map,而不是andThen
  • 如果它没有完全抛出异常并且没有到达我的onFailure回调,这将起作用。当我运行它时,我在代码执行中得到了一个堆栈跟踪,所以这不是预期的效果。
  • 我对@9​​87654331@ 的工作方式感到困惑。 map 函数将从A 类型的成功结果变为B 类型的成功结果。考虑到某种条件,我想让未来失败,这就是我一直使用flatMap 的原因。我很想看看map 在这里是如何应用的。
  • @cmbaxter:在这种情况下BA 是同一类型。 throw的结果是NothingNothing是所有类型的子类型,所以它也是A的子类型。
  • @cmbaxter:这里只有 1 个答案。 SO 是 wiki 资源,而不是 MMORPG。
【解决方案2】:

还有一个不同的解决方案(没有 @cmbaxter 提到的堆栈跟踪副作用)

val prom = Promise[Int]()
val f = future {
    1
}

f onComplete {
    case Success(i) if i < 5 => prom.failure(new RuntimeException("_ < 5"))
    case Success(x) => prom.success(x)
}

prom.future onComplete {
    case Success(s) => println(s"We cool '$s'")
    case Failure(th) => println(s"All failed, ${th.getMessage}")
}

【讨论】:

    【解决方案3】:

    最干净的解决方案是调用filter 方法,正如cmets 中提到的@senia。

    Future { Result(false) }.filter(_.successful) // throws NoSuchElementException
    

    如果您想要不同的异常类型,可以使用recoverWith 链接调用:

    Future { Result(false) }.filter(_.successful).recoverWith {
      case _: NoSuchElementException => Future.failed(new Exception("The call failed!!"))
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-27
      • 2019-04-21
      • 2013-07-07
      • 1970-01-01
      • 2017-05-22
      • 2021-12-27
      • 1970-01-01
      相关资源
      最近更新 更多