Scala Futures callback hell
I've been reading many times about Scala Futures reducing callback problems. I've got a code that started to look problematic.
val a = Future(Option(Future(Option(10))))
a.map { b =>
b.map { c =>
c.map { d =>
d.map { res =>
res + 10
}
}
}
}
How can I make this code more flat?
//Edit @againstmethod
for{
b <- a
c <- b
d <- c
res <- d
} yield res + 10
This code won't compile
Error:(21, 8) type mismatch; found : Option[Int] required:
scala.concurrent.Future[?] res <- d
^
You can use a for comprehension. In example:
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)
}
Where result will be 13.
Any class that implements a proper map
and flatMap
function can be used this way in a for
.
If you don't mind another dependency, you can also use a library like scalaz and explicitly use monadic binding to flatten things out (EDIT encoded some Option types to address a comment below):
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)
}
And finally, if you just have parallel operations that you want to bind after all have succeeded that are independent, you can use scalaz applicative builder:
val result = (
Future { 10 + 10 } |@|
Future { 20 - 3 } |@|
Future { Math.PI * 15 }
) { _ + _ / _}
println(Await.result(result, 1 seconds))
This will let all 3 futures complete, then apply block to the 3 arguments.
Actually the answer was pretty straight forward.
for {
a <- b
c <- a.get
} yield c.get + 10
Appears to be enough, because when x.get + 10
fails (because of None + 10
) the future just fails. So it still works to use a simple fallback
val f = for {
a <- b
c <- a.get
} yield c.get + 10
f fallbackTo Future.successful(0)
I haven't used them yet, but they should be exactly what you're looking for: Monad transformers.
Basically, a monad transformer takes a Monad (such as Future) and adds functionality to it, such as the functionality provided by Option, and returns a transformed Monad. I think there is even an Option Monad transformer in Scalaz. That should allow you to use nested Options in Futures and still have a flat code structure using for comprehensions.
See 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 some examples.
链接地址: http://www.djcxy.com/p/24670.html上一篇: 如何重新加载当前的铁路由器路由?
下一篇: 斯卡拉期货回调地狱