如何连接两个Scala期货

use*_*261 4 scala future

我有两个Future函数:

def parseIntFuture(str: String) = Future{scala.util.Try(str.toInt).toOption}
def divideFuture(a: Int, b: Int) = Future{ if (b == 0) None else Some(a / b)}
Run Code Online (Sandbox Code Playgroud)

现在我想连接它们并最终得到一个Future[Option[Int]]类型结果,这是第二个返回值,但如果我喜欢这样:

def stringDivideBy(aStr: String, bStr: String) = {
    val x = for {
        aNum <- parseIntFuture(aStr)
        bNum <- parseIntFuture(bStr)
    } yield (aNum, bNum)

    x.map(n => {
        for{
            a <- n._1
            b <- n._2
        } yield divideFuture(a, b)
    })
}
Run Code Online (Sandbox Code Playgroud)

实际上我会得到Future[Option[Future[Option[Int]]]]Future[Option[Int]]不仅仅是.我知道这是因为我将一个Future传递给另一个,但我不知道连接这两个Futures的正确方法是逐一避免使用Await.我停止明确使用Await,那么解决方案是什么?

Dim*_*ima 6

对于像这样简单的东西,你不需要monad变形金刚和其他"重型火炮".一般规则是不要让你的代码比它绝对必须更复杂.

 (parseIntFuture(foo) zip parseIntFuture(bar))
   .flatMap {
     case (Some(a), Some(b)) => divideFuture(a, b) 
     case _ => Future.successful(None)
   }
Run Code Online (Sandbox Code Playgroud)


And*_*kin 5

有一个叫做OptionTmonad变换器的东西可以解决这个问题.有了OptionT,你的代码看起来有点像

import cats.data.OptionT

// ...

val x = (for {
    aNum <- OptionT(parseIntFuture(aStr))
    bNum <- OptionT(parseIntFuture(bStr))
    res <- OptionT(divideFuture(aNum, bNum))
} yield res).value
Run Code Online (Sandbox Code Playgroud)

并返回一个Future[Option[Int]].


您可以以嵌套for-comprehensions为代价来避免monad变换器:

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

def parseIntFuture(str: String) = Future{scala.util.Try(str.toInt).toOption}
def divideFuture(a: Int, b: Int) = Future{ if (b == 0) None else Some(a / b)}

def stringDivideBy(aStr: String, bStr: String): Future[Option[Int]] = {
  for {
    aOpt <- parseIntFuture(aStr)
    bOpt <- parseIntFuture(bStr)
    resOpt <- 
      (for {
        a <- aOpt
        b <- bOpt
      } yield divideFuture(a, b))
      .getOrElse(Future { None })
  } yield resOpt
}
Run Code Online (Sandbox Code Playgroud)