Comment puis-je convertir Option[Future[T]]
en Future[Option[T]]
en scala?
Je veux l'utiliser dans:
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
Voici la méthode d'insertion de signature
def insert(address: Address): Future[Address]
ca
est un CustomerData
case class CustomerData(address: Address, invoiceAddress: Option[Address])
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)
}
Exemples:
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
La bibliothèque standard fournit les méthodes d'utilisation de Future.sequence sur une option. Malheureusement, vous devez les relier.
Soit comme une méthode rapide:
def swap[M](x: Option[Future[M]]): Future[Option[M]] =
Future.sequence(Option.option2Iterable(x)).map(_.headOption)
Remarque J'ai trouvé que le Option.option2Iterable
implicite était déjà dans la portée pour moi. Donc, vous n’avez peut-être pas besoin de le fournir, réduisant ainsi le code à Future.sequence(x).map(_.headOption)
Ou vous préférez peut-être une méthode d'extension:
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
Voici une autre solution:
def swap[T](o: Option[Future[T]]): Future[Option[T]] =
o.map(_.map(Some(_))).getOrElse(Future.successful(None))
L'astuce consiste à convertir Option[Future[T]]
en Option[Future[Option[T]]]
, ce qui est facile, puis d'extraire la valeur de cette Option
.
Lorsque vous avez une liste (ou une variable TraversableOnce
) de contrats à terme et que vous voulez un seul avenir pour calculer la liste complète, vous utilisez Future.sequence
ou Future.traverse
. Vous pouvez considérer une option comme une liste de 1 ou 0 éléments, mais comme ce n’est techniquement pas une liste, vous devez procéder à une petite conversion dans ce cas. Quoi qu'il en soit, c'est un code qui le fait normalement:
val optionFuture:Option[Future[String]] = ???
val futureOption:Future[Option[String]] = Future.sequence(optionFuture.toIterable).map(_.headOption)
Dans votre exemple, utilisez un meilleur 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
Si vous avez des chats comme dépendance dans votre application, le plus beau moyen serait d'utiliser 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)
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)