Scala Futures 回调地狱
Scala Futures callback hell
我已经多次阅读有关 Scala Futures 减少回调问题的文章。我的代码开始出现问题。
val a = Future(Option(Future(Option(10))))
a.map { b =>
b.map { c =>
c.map { d =>
d.map { res =>
res + 10
}
}
}
}
如何使这段代码更扁平化?
//编辑@againstmethod
for{
b <- a
c <- b
d <- c
res <- d
} yield res + 10
此代码无法编译
Error:(21, 8) type mismatch; found : Option[Int] required:
scala.concurrent.Future[?] res <- d
^
您可以使用 for comprehension。例如:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object Stuff extends App {
val result = for {
f1 <- Future { 10 + 1 }
f2 <- Future { f1 + 2 }
} yield f2
result.onComplete(println)
}
结果将为 13。
任何实现适当 map
和 flatMap
函数的 class 都可以在 for
.
中以这种方式使用
如果您不介意另一个依赖项,您还可以使用像 scalaz 这样的库并显式使用 monadic 绑定来使事情变平(编辑编码一些选项类型以解决下面的评论):
import scalaz._
import Scalaz._
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.util.{Success,Failure}
object BindEx extends App {
def f1(i: String): Future[Int] = Future { i.length }
def f2(i: Int): Future[Option[Double]] = Future { Some(i / Math.PI) }
def f3(i: Option[Double]): Future[Option[Double]] = Future {
i match {
case Some(v) => Some(Math.round(v))
case _ => None
}
}
val result =
Monad[Future].point("Starting Point") >>=
f1 >>=
f2 >>=
f3
result.onComplete { x =>
x match {
case Success(value) => println("Success " + value)
case Failure(ex) => println(ex)
}
}
Await.result(result, 1 seconds)
}
最后,如果您只有在所有独立的成功之后想要绑定的并行操作,您可以使用 scalaz applicative builder:
val result = (
Future { 10 + 10 } |@|
Future { 20 - 3 } |@|
Future { Math.PI * 15 }
) { _ + _ / _}
println(Await.result(result, 1 seconds))
这将使所有 3 个 futures 完成,然后将块应用于 3 个参数。
我还没有使用过它们,但它们应该正是您要找的:Monad 转换器。
基本上,一个 monad 转换器接受一个 Monad(例如 Future)并向其添加功能,例如 Option 提供的功能,以及 returns 一个转换后的 Monad。我认为 Scalaz 中甚至有一个 Option Monad 转换器。这应该允许您在 Futures 中使用嵌套选项,并且仍然具有用于理解的平面代码结构。
有关示例,请参阅 http://blog.garillot.net/post/91731853561/a-question-about-the-option-monad-transformer and https://softwarecorner.wordpress.com/2013/12/06/scalaz-optiont-monad-transformer/。
其实答案很简单。
for {
a <- b
c <- a.get
} yield c.get + 10
似乎足够了,因为当 x.get + 10
失败时(因为 None + 10
),未来就会失败。所以它仍然可以使用简单的回退
val f = for {
a <- b
c <- a.get
} yield c.get + 10
f fallbackTo Future.successful(0)
我已经多次阅读有关 Scala Futures 减少回调问题的文章。我的代码开始出现问题。
val a = Future(Option(Future(Option(10))))
a.map { b =>
b.map { c =>
c.map { d =>
d.map { res =>
res + 10
}
}
}
}
如何使这段代码更扁平化?
//编辑@againstmethod
for{
b <- a
c <- b
d <- c
res <- d
} yield res + 10
此代码无法编译
Error:(21, 8) type mismatch; found : Option[Int] required:
scala.concurrent.Future[?] res <- d
^
您可以使用 for comprehension。例如:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object Stuff extends App {
val result = for {
f1 <- Future { 10 + 1 }
f2 <- Future { f1 + 2 }
} yield f2
result.onComplete(println)
}
结果将为 13。
任何实现适当 map
和 flatMap
函数的 class 都可以在 for
.
如果您不介意另一个依赖项,您还可以使用像 scalaz 这样的库并显式使用 monadic 绑定来使事情变平(编辑编码一些选项类型以解决下面的评论):
import scalaz._
import Scalaz._
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.util.{Success,Failure}
object BindEx extends App {
def f1(i: String): Future[Int] = Future { i.length }
def f2(i: Int): Future[Option[Double]] = Future { Some(i / Math.PI) }
def f3(i: Option[Double]): Future[Option[Double]] = Future {
i match {
case Some(v) => Some(Math.round(v))
case _ => None
}
}
val result =
Monad[Future].point("Starting Point") >>=
f1 >>=
f2 >>=
f3
result.onComplete { x =>
x match {
case Success(value) => println("Success " + value)
case Failure(ex) => println(ex)
}
}
Await.result(result, 1 seconds)
}
最后,如果您只有在所有独立的成功之后想要绑定的并行操作,您可以使用 scalaz applicative builder:
val result = (
Future { 10 + 10 } |@|
Future { 20 - 3 } |@|
Future { Math.PI * 15 }
) { _ + _ / _}
println(Await.result(result, 1 seconds))
这将使所有 3 个 futures 完成,然后将块应用于 3 个参数。
我还没有使用过它们,但它们应该正是您要找的:Monad 转换器。
基本上,一个 monad 转换器接受一个 Monad(例如 Future)并向其添加功能,例如 Option 提供的功能,以及 returns 一个转换后的 Monad。我认为 Scalaz 中甚至有一个 Option Monad 转换器。这应该允许您在 Futures 中使用嵌套选项,并且仍然具有用于理解的平面代码结构。
有关示例,请参阅 http://blog.garillot.net/post/91731853561/a-question-about-the-option-monad-transformer and https://softwarecorner.wordpress.com/2013/12/06/scalaz-optiont-monad-transformer/。
其实答案很简单。
for {
a <- b
c <- a.get
} yield c.get + 10
似乎足够了,因为当 x.get + 10
失败时(因为 None + 10
),未来就会失败。所以它仍然可以使用简单的回退
val f = for {
a <- b
c <- a.get
} yield c.get + 10
f fallbackTo Future.successful(0)