Подтвердить что ты не робот

Сопоставьте будущее как успеха, так и неудачи

У меня есть Future [T], и я хочу сопоставить результат как с успехом, так и с отказом.

Например, что-то вроде

val future = ... // Future[T]
val mapped = future.mapAll { 
  case Success(a) => "OK"
  case Failure(e) => "KO"
}

Если я использую map или flatmap, он отображает только фьючерсы на успех. Если я использую recover, он отображает только несостоявшиеся фьючерсы. onComplete выполняет обратный вызов, но не возвращает измененное будущее. Transform будет работать, но он принимает 2 функции, а не частичную функцию, поэтому немного уродливее.

Я знаю, что могу создать новый Promise, и завершить это с помощью onComplete или onSuccess/onFailure, но я надеялся, что что-то у меня пропало, что позволит мне сделать это с помощью одиночный PF.

4b9b3361

Ответ 1

Изменить 2017-09-18: Начиная с Scala 2.12, существует метод transform, который принимает Try[T] => Try[S]. Таким образом, вы можете написать

val future = ... // Future[T]
val mapped = future.transform {
  case Success(_) => Success("OK")
  case Failure(_) => Success("KO")
}

Для 2.11.x ниже применяется следующее:

AFAIK, вы не можете сделать это напрямую с помощью одного PF. И transform преобразует Throwable = > Throwable, так что это тоже вам не поможет. Ближе всего вы можете выйти из окна:

val mapped: Future[String] = future.map(_ => "OK").recover(_ => "KO")

Тем не менее, реализация вашего mapAll тривиальна:

implicit class RichFuture[T](f: Future[T]) {
  def mapAll[U](pf: PartialFunction[Try[T], U]): Future[U] = {
    val p = Promise[U]()
    f.onComplete(r => p.complete(Try(pf(r))))
    p.future
  }
}

Ответ 2

На первом этапе вы можете сделать что-то вроде:

import scala.util.{Try,Success,Failure}

val g = future.map( Success(_):Try[T] ).recover{
  case t => Failure(t)
}.map {
  case Success(s) => ...
  case Failure(t) => ...
}

где T - тип будущего результата. Затем вы можете использовать неявное преобразование, чтобы добавить эту структуру в свойство Future как новый метод:

implicit class MyRichFuture[T]( fut: Future[T] ) {
  def mapAll[U]( f: PartialFunction[Try[T],U] )( implicit ec: ExecutionContext ): Future[U] = 
    fut.map( Success(_):Try[T] ).recover{
      case t => Failure(t)
    }.map( f )
 }

который реализует синтаксис, который вы ищете:

val future = Future{ 2 / 0 }
future.mapAll {
  case Success(i) => i + 0.5
  case Failure(_) => 0.0
}

Ответ 3

Оба варианта карты и flatMap:

implicit class FutureExtensions[T](f: Future[T]) {
  def mapAll[Target](m: Try[T] => Target)(implicit ec: ExecutionContext): Future[Target] = {
    val p = Promise[Target]()
    f.onComplete { r => p success m(r) }(ec)
    promise.future
  }

  def flatMapAll[Target](m: Try[T] => Future[Target])(implicit ec: ExecutionContext): Future[Target] = {
    val promise = Promise[Target]()
    f.onComplete { r => m(r).onComplete { z => promise complete z }(ec) }(ec)
    promise.future
  }
}

Ответ 4

Поскольку Scala 2.12, вы можете использовать transform для сопоставления обоих случаев:

future.transform {
      case Success(_) => Try("OK")
      case Failure(_) => Try("KO")
}

У вас также есть transformWith, если вы предпочитаете использовать Future вместо Try. Подробнее см. .