scala future

Opción de Scala[Futuro[T]] a Futuro[Opción



future (6)

Aquí hay otra solución:

def swap[T](o: Option[Future[T]]): Future[Option[T]] = o.map(_.map(Some(_))).getOrElse(Future.successful(None))

El truco es convertir la Option[Future[T]] en Option[Future[Option[T]]] que es fácil, y luego extraer el valor de esa Option .

¿Cómo puedo convertir la Option[Future[T]] a Future[Option[T]] en scala?

Quiero usarlo en:

val customerAddresses = for { a <- addressDAO.insert(ca.address) // Future[Address] ia <- ca.invoiceAddress.map(addressDAO.insert) // Option[Future[Address]] } yield (a, ia) // Invalid value have to be two futures

Aquí el método de inserción de la firma

def insert(address: Address): Future[Address]

ca es un CustomerData

case class CustomerData(address: Address, invoiceAddress: Option[Address])


Cuando tiene una lista (o cualquier TraversableOnce ) de futuros y desea un futuro único para calcular la lista completa, use Future.sequence o Future.traverse . Puede pensar en una Opción como una lista de 1 o 0 elementos, pero dado que técnicamente no es una lista, tiene que buscar una pequeña conversión en este caso. De todos modos, este es un código que lo hace normalmente:

val optionFuture:Option[Future[String]] = ??? val futureOption:Future[Option[String]] = Future.sequence(optionFuture.toIterable).map(_.headOption)

En tu ejemplo usa mejor Future.traverse :

val customerAddresses = for { a <- addressDAO.insert(ca.address) // Future[Address] ia <- Future.traverse(ca.invoiceAddress.toIterable)(addressDAO.insert).map(_.headOption) // Future[Option[Address]] } yield CustomerData(a, ia) // Types OK


La biblioteca estándar proporciona los métodos para usar Future.sequence en una Opción, desafortunadamente, tiene que sondearlos juntos.

Ya sea como un método rápido:

def swap[M](x: Option[Future[M]]): Future[Option[M]] = Future.sequence(Option.option2Iterable(x)).map(_.headOption)

Nota, encontré que el Option.option2Iterable implícito ya estaba dentro del alcance para mí. Por lo tanto, es posible que no necesite proporcionarlo, reduciendo el código a Future.sequence(x).map(_.headOption)

O puede que prefiera un método de extensión:

implicit class OptionSwitch[A](f: Option[Future[A]]) { import scala.concurrent.Future def switch: Future[Option[A]] = Future.sequence(Option.option2Iterable(f)) .map(_.headOption) } val myOpt = Option(Future(3)) myOpt.switch


Si tiene gatos como una dependencia en su aplicación, la forma más hermosa sería utilizar la traverse

import cats._ import cats.implicits._ val customerAddresses = for { a <- addressDAO.insert(ca.address) // Future[Address] ia <- ca.invoiceAddress.traverse(addressDAO.insert) // Future[Option[Address]] } yield (a, ia)


import scala.concurrent.Future import scala.concurrent.ExecutionContext def f[A](x: Option[Future[A]])(implicit ec: ExecutionContext): Future[Option[A]] = x match { case Some(f) => f.map(Some(_)) case None => Future.successful(None) }

Ejemplos:

scala> f[Int](Some(Future.successful(42))) res3: scala.concurrent.Future[Option[Int]] = Success(Some(42)) scala> f[Int](None) res4: scala.concurrent.Future[Option[Int]] = scala.concurrent.impl.Promise$KeptPromise@c88a337


val customerAddresses = for { a <- addressDAO.insert(ca.address) // Future[Address] ia <- ca.invoiceAddress.map(x => addressDAO.insert(x).map(_.map(k => Some(k))).getOrElse(Future.successful(None))) } yield (a, ia)