Не может сгладить Попытка в понимании

Это сочетание стилистического вопроса и моих попыток расширить понимание Scala.

У меня есть список, содержащий Future, я хочу вычислить значения фьючерсов, преобразовать их в Option и выровнять список, используя для понимания:

import scala.util.Try
import scala.concurrent._
import ExecutionContext.Implicits.global

val l= List(Future.successful(1),Future.failed(new IllegalArgumentException))

implicit def try2Traversible[A](xo: Try[A]): Iterable[A] = xo.toOption.toList

val res = for{f <- l; v <- f.value} yield v

scala> res: List[scala.util.Try[Int]] = List(Success(1), Failure(java.lang.IllegalArgumentException))

res.flatten
res16: List[Int] = List(1)

То, что я хочу сделать, - это получить плоскую сцену для понимания, у кого-нибудь есть какие-либо предложения?

3 ответа

Делать это неправильно:

for{f <- l; v <- f.value} yield v

Похоже, что работает в вашем случае только потому, что фьючерсы уже реализованы, поэтому их value член определен. Однако в общем случае они могут еще не быть выполнены при выполнении для понимания, и, таким образом, value вернусь None (несмотря на то, что в какой-то момент они в конечном итоге будут выполнены). Например, попробуйте это в REPL:

val f1 = Future{ 
  Thread.sleep(3000) // Just a test to illustrate, never do this!
  1
}
val f2 = Future{ 
  Thread.sleep(3000) // Just a test to illustrate, never do this!
  throw new IllegalArgumentException
}

val l = List( f1, f2 )
for{f <- l; v <- f.value} yield v

Результатом является пустой список, потому что ни один из фьючерсов в l выполнено еще. Затем подождите немного (не более 3 секунд) и повторите выполнение для понимания (последняя строка), и вы получите непустой список, потому что фьючерсы наконец-то выполнены.

Чтобы это исправить, вам придется либо заблокировать (то есть дождаться выполнения всех фьючерсов), используя scala.concurrent.Awaitили оставайтесь в асинхронном мире, используя что-то вроде Future.map или же Future.flatMap, Например, если вы хотите заблокировать, вы можете сделать:

Await.result( Future.sequence( l ), duration.Duration.Inf )

Await.result ждет результата будущего, позволяя перейти из асинхронного мира в синхронный мир. Результатом вышесказанного является List[Int]Проблема в том, что вы теряете случаи сбоев (результат не List[Try[Int]] как вы хотели), и фактически сбросит первое исключение. Чтобы исправить это, вы можете использовать этот вспомогательный метод, который я опубликовал в другом ответе: /questions/32386964/kak-prodolzhit-vyipolnenie-posledovatelnosti-future-nesmotrya-na-neudachu/32386975#32386975 Используя его, вы можете сделать:

Await.result( Future.sequence( l map mapValue ), duration.Duration.Inf )

Это будет ждать, пока все фьючерсы не будут выполнены (либо с правильным значением, либо с ошибкой) и вернет ожидаемое List[Try[Int]]

Идея состоит в том, чтобы перейти к Try объект, как будто это было Option (т. е. набор из 0 или 1 элемента) внутри самого понимания. Чтобы этот обход работал, должно быть преобразование из Try введите в Option тип.

Это должно работать:

implicit def try2option[A](xo: Try[A]) = xo.toOption

val res = for (f <- l; t <- f.value; x <- t) yield x

Вы должны держать Future вокруг вашего окончательного результата, чтобы сохранить асинхронный характер вычислений.

Хороший способ сделать это (и получить Future[List[Int]]) будет (вероятно, что вы пытались):

for {
  f <- l    // Extract individual future
  v <- f    // Extract value from future
} yield v

К сожалению, это означает:

l.flatMap(f => f.map(v => v))

Который не работает, потому что Future не наследует GenTraversableOnce (и, вероятно, не следует), но List нужна эта черта для его flatMap,

Однако мы можем сделать это вручную: val res = l.foldRight(Future.successful(List.empty[Int])) { case (x,xs) => xs.flatMap(vxs => x.map(vx =>) vx:: vxs)) }


Мы можем использовать Future.sequence сделать это:

Future.sequence(l)

Это вернет Future[List[Int]] которая завершается только после завершения всех фьючерсов и будет содержать все значения фьючерсов, которые были успешно завершены.

Другие вопросы по тегам