如何连接两个 Scala Futures
How to connect two Scala Futures
我有两个 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)}
现在我想连接它们并最终得到一个 Future[Option[Int]]
类型的结果,这是第二个的 return 值,但如果我这样做:
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)
})
}
实际上我只会得到 Future[Option[Future[Option[Int]]]]
而不是 Future[Option[Int]]
。我知道这是因为我将一个 Future 传递给另一个,但我不知道将这两个 Future 一个接一个地连接起来避免使用 Await
的正确方法是什么。我停止明确使用等待,那么解决方案是什么?
有一个叫做 OptionT
monad transformer 的东西正好解决了这个问题。使用 OptionT
,您的代码看起来有点像
import cats.data.OptionT
// ...
val x = (for {
aNum <- OptionT(parseIntFuture(aStr))
bNum <- OptionT(parseIntFuture(bStr))
res <- OptionT(divideFuture(aNum, bNum))
} yield res).value
和return一个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
}
像这样简单的东西,您不需要 monad 转换器和其他 "heavy artillery"。一般规则是不要让你的代码比它绝对必须的更复杂。
(parseIntFuture(foo) zip parseIntFuture(bar))
.flatMap {
case (Some(a), Some(b)) => divideFuture(a, b)
case _ => Future.successful(None)
}
我有两个 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)}
现在我想连接它们并最终得到一个 Future[Option[Int]]
类型的结果,这是第二个的 return 值,但如果我这样做:
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)
})
}
实际上我只会得到 Future[Option[Future[Option[Int]]]]
而不是 Future[Option[Int]]
。我知道这是因为我将一个 Future 传递给另一个,但我不知道将这两个 Future 一个接一个地连接起来避免使用 Await
的正确方法是什么。我停止明确使用等待,那么解决方案是什么?
有一个叫做 OptionT
monad transformer 的东西正好解决了这个问题。使用 OptionT
,您的代码看起来有点像
import cats.data.OptionT
// ...
val x = (for {
aNum <- OptionT(parseIntFuture(aStr))
bNum <- OptionT(parseIntFuture(bStr))
res <- OptionT(divideFuture(aNum, bNum))
} yield res).value
和return一个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
}
像这样简单的东西,您不需要 monad 转换器和其他 "heavy artillery"。一般规则是不要让你的代码比它绝对必须的更复杂。
(parseIntFuture(foo) zip parseIntFuture(bar))
.flatMap {
case (Some(a), Some(b)) => divideFuture(a, b)
case _ => Future.successful(None)
}