Scala Future:将 Future[Vector[T]] 转换为 Future[Result]

Scala Future: Transform Future[Vector[T]] to Future[Result]

我有一个方法可以创建类型为 VectorFuture,然后我想从中创建适当的 HTTP 响应。

到目前为止,我已经尝试使用 Futures API 中的 transformtransformWith 方法来实现这一点,主要是因为它们的参数具有 Try[T] 签名,并且我分得清成功与失败

我们假设这是生成 Future 类型 Vector

的方法
def aCoupleOfFutures: Future[Vector[String]] = ???

使用这种方法可以注意到 Try 结构的重复使用

val foo:Future[Result] = aCoupleOfFutures.transform {
    case Success(strings) => Try(Created(json.Json.toJson(strings)))
    case Failure(e) => Try(BadRequest(e.getMessage))
}

使用这种方法可以注意到 Future.successful 嵌套的重复使用。

val foo:Future[Result] = aCoupleOfFutures.transformWith {
    case Success(strings) => Future.successful(Created(json.Json.toJson(strings)))
    case Failure(e) => Future.successful(BadRequest(e.getMessage))
}

使用这种方法,当服务产生异常时,我无法 return 所需的 BadRequest。由于重载的 transform 方法的签名是 (def transform[S](s: T => S, f: Throwable => Throwable))

val foo:Future[Result] = aCoupleOfFutures.transform (
    options => Created(json.Json.toJson(options)),
    exc => exc
)

所以我的问题是:我可以有类似下面的东西吗:

val result:Future[Result] = aCoupleOfFutures. someKindOfTransform {
    case Success(options) => Created(json.Json.toJson(options))
    case Failure(e) => BadRequest(e.getMessage)
}

最简单的选择似乎是将其分成 maprecover 步骤:

val result = aCoupleOfFutures
  .map(options => Created(json.Json.toJson(options)))
  .recover{ case exc => BadRequest(exc.getMessage)}

你是对的,似乎没有一个单一的转换方法可以做你想做的事 - 最接近的签名似乎是 onComplete,但是 returns Unit 因此不允许您生成 return 值。也就是说,将两者分开并不一定是坏事,因为您通常希望在所有控制器中抽象出 error-handling 逻辑。

折叠操作在这里会有用-

def fold[U](fa: Throwable => U, fb: T => U): U

这是一个终端操作,您想将所有结果或异常转换为Result。 try/either/option 上的折叠操作会将处于任何状态的整个事物转换为类型 U.

val result = aCoupleOfFutures
  .transform(
    tryResult =>
      Success(
        tryResult.fold(th => BadRequest(th.getMessage), options => Created(json.Json.toJson(options)))
      )
  )