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

Scala - Цепочные фьючерсы Попробуйте блокировать?

Возможно ли цепочка scala.util.Try и scala.concurrent.Future? Оба они эффективно обеспечивают один и тот же монадический интерфейс, но попытка их объединения приводит к ошибке компиляции.

Например. Учитывая две подписи ниже

def someFuture:Future[String] = ???
def processResult(value:String):Try[String] = ???

Можно ли сделать что-то вроде следующего?

val result = for( a <- someFuture; b <- processResult( a ) ) yield b;
result.map { /* Success Block */ } recover { /* Failure Block */ }

Это, очевидно, приводит к ошибке компиляции, потому что Future и Try не могут быть FlatMapp'ed вместе.

Однако было бы хорошей возможностью, чтобы иметь возможность связывать их - это вообще возможно? Или мне нужно объединить их в будущее [Try [String]]?

(В частности, меня интересует наличие одного блока "восстановления" для исключения исключений в будущем или в попытке).

4b9b3361

Ответ 1

При столкновении с такой проблемой, когда вы хотите использовать разные типы в понимании, одним из решений может быть попытка выбрать один из типов и сопоставить другой тип с ним. Для вашей ситуации, учитывая уникальные свойства (асинхронный) фьючерсов, я бы выбрал Future как самый низкий общий знаменатель и сопоставил Try с Future. Вы можете просто сделать это вот так:

val result = for{
  a <- someFuture
  b <- tryToFuture(processResult(a)) 
}  yield b
result.map { /* Success Block */ } recover { /* Failure Block */ }

def tryToFuture[T](t:Try[T]):Future[T] = {
  t match{
    case Success(s) => Future.successful(s)
    case Failure(ex) => Future.failed(ex)
  }
}

Теперь, если вы обнаружили, что это очень распространенная ситуация, и вам не нравилось постоянно добавлять явное преобразование, я полагаю, вы могли бы определить метод tryToFuture как неявный для некоторого вспомогательного объекта и импортировать его там, где это необходимо это:

object FutureHelpers{
  implicit def tryToFuture[T](t:Try[T]):Future[T] = {
    t match{
      case Success(s) => Future.successful(s)
      case Failure(ex) => Future.failed(ex)
    }
  }
}

import FutureHelpers._
val result = for{
  a <- someFuture
  b <- processResult(a) 
}  yield b
result.map { /* Success Block */ } recover { /* Failure Block */ }

Просто помните, что вызов Future.success и Future.failed влияет на то, что ExecutionContext находится в области видимости в том, что он подчинит ему еще одну задачу под капотом.

ИЗМЕНИТЬ

Как отметил Виктор в комментариях, процесс преобразования a Try в Future еще проще, если вы просто используете Future.fromTry, как в обновленном примере ниже:

val result = for{
  a <- someFuture
  b <- Future.fromTry(processResult(a)) 
}  yield b
result.map { /* Success Block */ } recover { /* Failure Block */ }

Это, пожалуй, лучший выбор для вас, если вы делаете что-то с импликацией или сворачиваете свою собственную логику преобразования.

Ответ 2

Как насчет

val result = for( a <- someFuture) yield for( b <- processResult( a ) ) yield b;

Хотя он не выглядит аккуратным.

Ответ 3

Возможно, проблема устарела, но в настоящее время вы можете:

  implicit def tryToFuture[T](t:Try[T]):Future[T] = Promise.fromTry(t).future

Ответ 4

  implicit def convFuture[T](ft: Future[Try[T]]): Future[T] =
ft.flatMap {
  _ match {
    case Success(s) => Future.successful(s)
    case Failure(f) => Future.failed(f)
  }
}

Ответ 5

Существует также

Future.fromTry(Try { ... })

Итак, вы могли бы сделать

 val result = for {
   a <- someFuture
   b <- Future.fromTry(processResult(a)) 
 } yield b;