import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.concurrent.Future
val future = Future {
"Hello" + "World"
}
future foreach println
These operations are asynchronous ... the println only runs when the Future completes
// Already "done" successfully Future
val future = Future.successful("Yay!")
// Already "failed" Future
val otherFuture = Future.failed[String](
new IllegalArgumentException("Bang!")
)
val promise = Promise[String]()
val theFuture = promise.future
promise.success("hello")
Promises are "for later" and can provide a completed Future
val f1 = Future {
"Hello" + "World"
}
val f2 = f1 map { x =>
x.length
}
f2 foreach println
map does not mutate the Future, but provides a new one... and doesn't block!
val f1 = Future {
"Hello" + "World"
}
val f2 = Future.successful(3)
val f3 = f1 map { x =>
f2 map { y =>
x.length * y
}
}
f3 foreach println
Double mapping gives us a Future[Future[Int]]...
Not good.
val f1 = Future {
"Hello" + "World"
}
val f2 = Future.successful(3)
val f3 = f1 flatMap { x =>
f2 map { y =>
x.length * y
}
}
f3 foreach println
Using flatMap will flatten us out...
But it's probably better to use for comprehensions here.
val future1 = Future.successful(4)
val future2 = future1.filter(_ % 2 == 0)
future2 foreach println
val failedFilter = future1.filter(_ % 2 == 1).recover {
// When filter fails, it will have a
// java.util.NoSuchElementException
case m: NoSuchElementException => 0
}
failedFilter foreach println
If we only want to continue with a Future under certain conditions, filter helps us out
val f = for {
a <- Future(10 / 2) // 10 / 2 = 5
b <- Future(a + 1) // 5 + 1 = 6
c <- Future(a - 1) // 5 - 1 = 4
if c > 3 // Future.filter
} yield b * c // 6 * 4 = 24
// Note that the execution of futures a, b, and c
// are not done in parallel.
f foreach println
Because for comprehensions use map, flatMap, and filter, execution is sequential here.
// oddActor returns odd numbers sequentially from 1 as a List[Future[Int]]
val listOfFutures = List.fill(100)(
akka.pattern.ask(oddActor, GetNext).mapTo[Int]
)
// now we have a Future[List[Int]]
val futureList = Future.sequence(listOfFutures)
// Find the sum of the odd numbers
val oddSum = futureList.map(_.sum)
oddSum foreach println
sequence transforms a List[Future[T]] into a Future[List[T]]
val futureList = Future.sequence((1 to 100).toList.map { x =>
Future(x * 2 - 1))
}
val oddSum = futureList.map(_.sum)
oddSum foreach println
The only problem here is sequence creates an intermediate List[Future[Int]]
val futureList = Future.traverse((1 to 100).toList) { x =>
Future(x * 2 - 1)
}
val oddSum = futureList.map(_.sum)
oddSum foreach println
traverse will create a Traversable[Future[T]] without an intermediate List step here
// Create a sequence of Futures
val futures = for (i <- 1 to 1000) yield Future(i * 2)
val futureSum = Future.fold(futures)(0)(_ + _)
futureSum foreach println
fold here works just like foldLeft
// Create a sequence of Futures
val futures = for (i <- 1 to 1000) yield Future(i * 2)
val futureSum = Future.reduce(futures)(_ + _)
futureSum foreach println
reduce is useful when we don't have a start value, and instead want to "start" with the result of the first Future
future onSuccess {
case "bar" => println("Got my bar alright!")
case x: String => println("Got some random string: " + x)
}
onSuccess only runs when no Exception is thrown...
future onFailure {
case ise: IllegalStateException if ise.getMessage == "OHNOES" =>
//OHNOES! We are in deep trouble, do something!
case e: Exception =>
//Do something else
}
onFailure can "catch" any Exception ...
Note that Future does not "throw" outside itself!
future onComplete {
case Success(result) => doSomethingOnSuccess(result)
case Failure(failure) => doSomethingOnFailure(failure)
}
onComplete always runs when a Future completes, passing a scala.util.Try (instances of Success[T] or Failure[Throwable])
val future4 = future1 fallbackTo future2 fallbackTo future3
future4 foreach println
fallbackTo combines two Future instances, holding the successful value of the second Future if the first fails
val future3 = future1 zip future2 map { case (a, b) =>
a + " " + b
}
future3 foreach println
We can also zip two Future instances together creating a Tuple of successful results
val future = akka.pattern.ask(actor, msg1) recover {
case e: ArithmeticException => 0
}
future foreach println
recover lets us return a "default" value in the case of an Exception
val future = akka.pattern.ask(actor, msg1) recoverWith {
case e: ArithmeticException => Future.successful(0)
case foo: IllegalArgumentException =>
Future.failed[Int](new IllegalStateException("All br0ken!"))
}
future foreach println
recoverWith is to flatMap as recover is to map...
Useful when you want to "recover" with another Future