Published 2014-09-22.
Last modified 2019-07-01.
Time to read: 12 minutes.
This lecture consists of a discussion and a code example for every important method provided by the Scala Future
class and companion object. This lecture is the culmination of a long sequence of information that started with the Introduction to Scala course and cross-references to other lectures are provided to help you understand the code examples.

You may remember from the Combinators lecture earlier in this course that monads hold values,
and Future
is just a regular monad like all others.
Performing an operation on a monad yields another monad of the same type.
That same lecture taught you that combinators are methods that perform transformations on the contents of monads. Futures are composable, which means:
- Operations on a
Future
or a collection ofFuture
s can be chained together without blocking. - Transformations such as
map
andflatMap
can be applied to collections ofFuture
s. - Collections of
Future
s can be manipulated (for example, reduced).
Future
s are immutable, so the result of transforming a Future
with a combinator is a new Future
.
Chaining multiple combinators together results in intermediate Future
s being created.
Some combinators accept partial functions; as you know from the Partial Functions lecture,
partial functions which each handle a subset of possible input values or types can be chained together,
and the resulting chain of partial functions is able to handle the union of all the input types and values of the individual partial functions.
When many combinators are applied to Future
s, however the cost is that a new intermediate Future
will be created for each combinator, and that means more expensive context switching.
When the partial functions are computationally expensive,
the extra overhead of the additional context switches is not noticeable,
however a chain of lightweight partial functions would be more efficient if they were consolidated into one partial function with many case statements.
Examples of this are shown where appropriate in this lecture.
The sample code for this lecture can be found in
courseNotes/
.
Many of the methods shown require an ExecutionContext
;
the code shown on this page assumes that an ExecutionContext
is provided.
As we discussed in the MultiThreading lecture,
one way to provide an ExecutionContext
is to place the following before the methods that require it:
import concurrent.ExecutionContext.Implicits.global
Some of the code example use postfix notation. You can enable postfix notation with the following:
import language.postfixOps
Exceptions encountered in this lecture can be imported as follows.
import java.io.{FileNotFoundException, IOException} import java.util.concurrent.TimeoutException import java.net.{MalformedURLException, UnknownHostException}
Some of the code examples require random numbers. This random number generator was used:
val random = new util.Random()
Combinators for Handling Failure
This section introduces the failure-related combinators. The code examples in this section rely on the following variable values:
lazy val goodUrlStr1 = "https://www.scalacourses.com" lazy val goodUrlStr2 = "https://www.micronauticsresearch.com" lazy val badHostUrlStr = "https://www.not-really-here.com" lazy val badPageUrlStr = "https://scalacourses.com/noPageHere" lazy val badProtocolUrlStr = "blah://scalacourses.com" lazy val badHostFuture: Future[String] = readUrlFuture(badHostUrlStr) lazy val badPageFuture: Future[String] = readUrlFuture(badHostUrlStr) lazy val badProtocolFuture: Future[String] = readUrlFuture(badHostUrlStr) lazy val defaultFuture: Future[String] = readUrlFuture(goodUrlStr1)
readUrlFuture
is a method defined in the multi
package object.
Package objects were introduced in the Scala Imports and Packages lecture of the
Introduction to Scala course.
This asynchronous method returns a Future
of the contents of the web page pointed to by the urlStr
parameter.
The method is said to be asynchronous because it returns a Future
.
If the web page is longer than the optional maxChars
parameter, it is truncated and ellipses are appended to indicate truncation.
/** @return up to first maxChars characters of web page at given url */ def readUrl(url: String, maxChars: Int=500): String = { val contents = io.Source.fromURL(url).mkString.trim contents.substring(0, math.min(contents.length, maxChars)) + (if (contents.length>maxChars) "..." else "") }
/** @return Future of first maxChars characters of web page at given url */ def readUrlFuture(urlStr: String, maxChars: Int=500): Future[String] = Future(readUrl(urlStr, maxChars))
Some of the code examples also use the show
method.
Take a moment to study it; you should be able to understand what it does by this point in the course.
/** Blocks until contents of web page at urlStr or whatever recoveryFn contains becomes available, then prints contents. * @param urlStr String passed to java.net.URL that specifies the URL of a web page * @param msg Optional parameter that specifies a prefix message to be displayed before the web page contents * @param recoveryFn Function1[Future[String], Future[String]] which is a pluggable recovery strategy for failed web pages * @return Future of contents of web page at urlStr or recoverFn */ def show(urlStr: String, msg: String="") (recoveryFn: Future[String] => Future[String]): Future[String] = { val future = recoveryFn(readUrlFuture(urlStr)) println(s"$urlStr; ${ if (msg.length>0) s"$msg, " else "" }returning " + Await.result(future, 30 minutes)) future }
The show
method’s recoveryFn
parameter has type Function1[Future[String] => Future[String]]
,
which is a recovery strategy for failed web pages.
If this parameter's type is not familiar to you, please review the Functions are First Class and
Lambda Review & Drill lectures of the
Introduction to Scala course.
This method allows us to write some succinct and efficient code that is easy to work with.
The method returns the Future
returned by recoverFn
.
Without further ado, following are the Future methods that I promised. The name of each method is followed by its signature on the next line:
-
fallbackTo
fallbackTo[U >: T](given: Future[U]): Future[U] -
fallbackTo
creates a newFuture
which inherits the value of the originalFuture
if it completes successfully, or if the originalFuture
fails, the result of the givenFuture
if it completes successfully. If both the original and the givenFuture
s fail, the resultingFuture
inherits theThrowable
from the originalFuture
.Here is an example that shows this combinator in action, using dot notation and infix notation. The third usage of
fallbackTo
results in a failedFuture
because both the originalFuture
and the fallbackFuture
fail.Scala codeobject FutureFallbackTo extends App { import multi.futures.FutureFixtures._
println("Dot notation:\n" + Await.result(badHostFuture.fallbackTo(defaultFuture), 30 minutes)) println("\n\nInfix notation:\n" + Await.result(badHostFuture fallbackTo defaultFuture, 30 minutes)) println("\n\n") Await.result(badHostFuture fallbackTo badPageFuture, 30 minutes) }Because
Await.result
is the last expression in the program, and the result is anException
, theException
is returned by the program.You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureFallbackTo" Dot notation: <!DOCTYPE html> <html lang="en"> <head> <title>Online Scala Training</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script src='https://cdn.jsdelivr.net/webjars/jquery/1.11.1/jquery.min.js'></script> <script src='https://cdn.jsdelivr.net/webjars/jquery-cookie/1.4.1/jquery.cookie.js'></script> <script src='https://cdn.jsdelivr.net/webjars/jquery-ui/1.11.1/jquery-ui.min.js'></script> <script src='https://cdn.jsdelivr.net/webjars/bootstrap/2.3.2/js/bootstrap....
Infix notation: <!DOCTYPE html> <html lang="en"> <head> <title>Online Scala Training</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script src='https://cdn.jsdelivr.net/webjars/jquery/1.11.1/jquery.min.js'></script> <script src='https://cdn.jsdelivr.net/webjars/jquery-cookie/1.4.1/jquery.cookie.js'></script> <script src='https://cdn.jsdelivr.net/webjars/jquery-ui/1.11.1/jquery-ui.min.js'></script> <script src='https://cdn.jsdelivr.net/webjars/bootstrap/2.3.2/js/bootstrap....
Exception in thread "main" java.net.UnknownHostException: www.not-really-here.com ... -
recover
recover[U >: T](pf: PartialFunction[Throwable, U])
(implicit executor: ExecutionContext): Future[U] -
recover
creates a newFuture
from a partial function that receives anyThrowable
that the original failedFuture
might contain. If the originalFuture
was successful, the newFuture
will have the same value as the originalFuture
. If the partial function does not handle the original failedFuture
'sThrowable
, the newFuture
will inherit the unhandledThrowable
from the originalFuture
.The example recovery strategies for this combinator all call
doSomething
, which is intended to suggest to you that you could write whatever code is appropriate in order to handle each type ofException
. Here isdoSomething
in all its glory; as you can see, it merely returns theString
it receives.Scala codedef doSomething(msg: String): String = msg
Here is an example of a
Future
that does not fail. Remember that the recovery strategy is passed into theshow
method, which is a higher-order function. In this example, therecover
method is not invoked.Scala codeshow(goodUrlStr1, "no problem") { _.recover { case e: UnknownHostException => doSomething("Handled UnknownHostException") } }
Output is:
Outputhttps://www.scalacourses.com; no problem, returning <!DOCTYPE html> <html lang="en"> <head> <title>Online Scala Training</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script src='https://cdn.jsdelivr.net/webjars/jquery/1.11.1/jquery.min.js'></script> <script src='https://cdn.jsdelivr.net/webjars/jquery-cookie/1.4.1/jquery.cookie.js'></script> <script src='https://cdn.jsdelivr.net/webjars/jquery-ui/1.11.1/jquery-ui.min.js'></script> <script src='https://cdn.jsdelivr.net/webjars/bootstrap/2.3.2/js/bootstrap....
Here is an example how
recover
handles aFuture
that fails:Scala codeshow(badHostUrlStr) { _.recover { case e: UnknownHostException => doSomething("Handled UnknownHostException") } }
Output is:
Outputhttps://www.not-really-here.com; returning Handled UnknownHostException
Here is an example of using
recover
in a partial function that does not handle theException
that is actually thrown.Scala codetry { show(badHostUrlStr) { _.recover { case e: NoSuchElementException => throw new Exception("Should not need to handle NoSuchElementException") } } } catch { case e: Exception => println(s"Did not handle ${e.getClass.getName} exception for $badHostUrlStr") }
Output is:
OutputDid not handle java.net.UnknownHostException exception for https://www.not-really-here.com
You can specify multiple
recover
clauses, each with a different partial function. The Scala compiler knows thatMalformedURLException
is never thrown so it issues an "unreachable code
" warning for that case.Scala codeshow(badHostUrlStr, "handle 4 Exception types in 4 PartialFunctions using recover") { _.recover { case e: FileNotFoundException => doSomething("Handled FileNotFoundException") } .recover { case e: IOException => doSomething("Handled IOException") } .recover { case e: MalformedURLException => doSomething("Handled MalformedURLException") } .recover { case e: UnknownHostException => doSomething("Handled UnknownHostException") } }
Output is:
Outputhttps://www.not-really-here.com; handle 4 Exception types in 4 PartialFunctions using recover, returning Handled IOException
Because each
recover
invocation above creates a newFuture
, there will be more context switching than if only one partial function with multiple case statements was supplied to onerecover
invocation, as shown in this next example. Again, the Scala compiler knows thatMalformedURLException
is never thrown so it issues an "unreachable code
" warning for that case.Scala codeshow(badHostUrlStr, "handle 4 Exception types in one PartialFunction using recover") { _.recover { case e: FileNotFoundException => doSomething("Handled FileNotFoundException") case e: IOException => doSomething("Handled IOException") case e: MalformedURLException => doSomething("Handled MalformedURLException") case e: UnknownHostException => doSomething("Handled UnknownHostException") } }
Output is:
Outputhttps://www.not-really-here.com; handle 4 Exception types in one PartialFunction using recover, returning Handled IOException
You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureRecover"
Output is as shown above.
-
recoverWith
recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] -
RecoverWith
is likerecover
, but the partial function references anotherFuture
, instead of a value that will be wrapped up into aFuture
, and the value of thatFuture
is used to create the returnedFuture
if therecover
clause is executed.Here is an example of a successful
Future
, so the partial function supplied to therecoverWith
combinator is not executed.Scala codeshow(goodUrlStr1, "no problem") { _.recoverWith { case e: UnknownHostException => Future.successful("Handled UnknownHostException") } }
Output is:
Outputhttps://www.scalacourses.com; no problem, returning <!DOCTYPE html> <html lang="en"> <head> <title>Online Scala Training</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script src='https://cdn.jsdelivr.net/webjars/jquery/1.11.1/jquery.min.js'></script> <script src='https://cdn.jsdelivr.net/webjars/jquery-cookie/1.4.1/jquery.cookie.js'></script> <script src='https://cdn.jsdelivr.net/webjars/jquery-ui/1.11.1/jquery-ui.min.js'></script> <script src='https://cdn.jsdelivr.net/webjars/bootstrap/2.3.2/js/bootstrap....
Here is an example of a
Future
that fails, so the partial function supplied to therecoverWith
combinator is invoked.Scala codeshow(badHostUrlStr) { _.recoverWith { case e: UnknownHostException => Future.successful("Handled UnknownHostException") } }
Output is:
Scala codehttps://www.not-really-here.com; returning Handled UnknownHostException Did not handle java.net.UnknownHostException exception for https://www.not-really-here.com
Here is an example of using
recoverWith
with a partial function that does not handle theException
that is actually thrown. In the following code, the Scala compiler knows thatNoSuchElementException
is never thrown so it issues an "unreachable code
" warning for that case.Scala codetry { show(badHostUrlStr) { _.recoverWith { case e: NoSuchElementException => Future.failed(new Exception("Should not need to handle NoSuchElementException")) } } } catch { case e: Exception => println(s"Did not handle ${e.getClass.getName} exception for $badHostUrlStr") }
Output is:
Scala codeDid not handle java.net.UnknownHostException exception for https://www.not-really-here.com
You can specify multiple
recoverWith
clauses, each with a different partial function. The Scala compiler knows thatMalformedURLException
is never thrown, so it issues an "unreachable code
" warning for that case.Scala codeshow(badHostUrlStr, "handle 4 Exception types in 4 PartialFunctions using recoverWith") { _.recoverWith { case e: FileNotFoundException => defaultFuture} .recoverWith { case e: IOException => defaultFuture} .recoverWith { case e: MalformedURLException => defaultFuture} .recoverWith { case e: UnknownHostException => defaultFuture} }
Output is:
Outputhttps://www.not-really-here.com; handle 4 Exception types in 1 PartialFunction using recoverWith, returning <!DOCTYPE html> <html lang="en"> <head> <title>Online Scala Training</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <script src='https://cdn.jsdelivr.net/webjars/jquery/1.11.1/jquery.min.js'></script> <script src='https://cdn.jsdelivr.net/webjars/jquery-cookie/1.4.1/jquery.cookie.js'></script> <script src='https://cdn.jsdelivr.net/webjars/jquery-ui/1.11.1/jquery-ui.min.js'></script> <script src='https://cdn.jsdelivr.net/webjars/bootstrap/2.3.2/js/bootstrap....
The above code is equivalent to the following:
Scala codeshow(badHostUrlStr, "handle 4 Exception types in 1 PartialFunction using recoverWith") { _.recoverWith { case e: FileNotFoundException => defaultFuture case e: IOException => defaultFuture case e: MalformedURLException => defaultFuture case e: UnknownHostException => defaultFuture } }
Output is the same as before. You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureRecoverWith"
Output is as shown above.
Combinators for Individual Futures
-
collect
collect[S](pf: PartialFunction[T, S])
(implicit executor: ExecutionContext): Future[S] -
collect
is likemap
, except it accepts a partial function, as we discussed in the Partial Functions lecture. Ifrecover
orrecoverWith
are chained aftercollect
, values not handled by the partial function can be dealt with specially.Scala codeval allUrlsWithFutures: List[(String, Future[String])] = urls(includeBad = true) map { url => url -> readUrlFuture(url) }
allUrlsWithFutures foreach { case tuple: (String, Future[String]) => println(s" Examining ${tuple._1}") tuple._2.collect { case content: String if content.toLowerCase.contains("scala") => println(s" Using Future.collect, scala was found in ${tuple._1}") }.recover { case _ => println(s" Future for ${tuple._1} failed") } }
val futures = Future.sequence(allUrlsWithFutures.map { _._2 }) Await.ready(futures, 30 minutes)Future[List[String]]
which is used by the last line to determine when to stop blocking the main thread so the program can exit. TheFuture.sequence
combinator is described later in this lecture. You can run this program by typing:Shell$ sbt "runMain multi.futures.FutureCollect" Examining https://www.scalacourses.com Examining https://www.scalacourses.com Examining https://www.not-really-here.com Examining https://scalacourses.com/noPageHere Examining blah://scalacourses.com Future for blah://scalacourses.com failed Future for https://www.not_really_here.com failed Future for https://scalacourses.com/noPageHere failed Using Future.collect, scala was found in https://www.scalacourses.com
Note that the last line of the program is a call to
Await.ready
, which returnsUnit
. This becomes the returned value of the console application. IfAwait.result
is called instead, theException
from the first failedFuture
would always be reported. -
filter
filter(p: T ⇒ Boolean)(implicit executor: ExecutionContext): Future[T] -
filter
creates a newFuture
that contains the originalFuture
's value if the predicate succeeds, or returns a newFuture
whose value isjava.util.NoSuchElementException("Future.filter predicate is not satisfied")
if the predicate fails. Ifrecover
orrecoverWith
are chained afterfilter
, values that failed the predicate can be dealt with.Scala code1 to 10 foreach { i => val future: Future[Int] = Future.successful(random.nextInt(100)) val oddFuture: Future[Int] = future filter { _ % 2 == 1 } val evenFuture: Future[Int] = future filter { _ % 2 == 0 } val oddOr24 = Await.result(oddFuture.recover { case throwable: Throwable => 24}, 30 seconds) val evenOr42 = Await.result(evenFuture.recover { case throwable: Throwable => 42}, 30 seconds) val all = Await.result(evenFuture.recoverWith { case throwable: Throwable => oddFuture }, 30 seconds) println(f"$i%2.0f oddOr24=$oddOr24; evenOr42=$evenOr42; all=$all") }
Either
oddFuture
orevenFuture
must containFailure(java.util.NoSuchElementException: Future.filter predicate is not satisfied)
. TheoddOr24
andevenOr42
variables are created by calling recover on the potentially failedFuture
s, and provided success values24
and42
, respectively, in place of failedException
s. Since we know that all numbers are either even or odd, we can define theall
variable that will contain either the even or the odd number by callingevenFuture.recoverWith
to provide the value ofoddFuture
in place of theNoSuchElementException
.You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureFilter" 1: oddOr24=24; evenOr42=54; all=54 2: oddOr24=24; evenOr42=70; all=70 3: oddOr24=24; evenOr42=86; all=86 4: oddOr24=24; evenOr42=60; all=60 5: oddOr24=89; evenOr42=42; all=89 6: oddOr24=24; evenOr42=86; all=86 7: oddOr24=24; evenOr42=70; all=70 8: oddOr24=39; evenOr42=42; all=39 9: oddOr24=37; evenOr42=42; all=37 10: oddOr24=24; evenOr42=10; all=10
-
flatMap
flatMap[S](f: T ⇒ Future[S])(implicit executor: ExecutionContext): Future[S] -
flatMap
is useful for applying an asynchronous method to aFuture
, and it yields a newFuture
containing the result. Asynchronous methods were introduced in the Futures & Promises lecture. Scala translates for-expressions intoflatMap
andmap
calls as discussed in the For-comprehensions With Futures lecture.This code example requires a
User
case class, which has has two properties:name
andprivilege
. We will useflatMap
in a functional-style program that first pretends to retrieve aUser
instance from a database. Because this make-believe database has an asynchrous API, theUser.findById
method returns aFuture[User]
. User privilege is augmented by callinggrantPrivilege
, which is also an asynchronous call involving the make-believe database, so it also returns aFuture[User]
. InsidegrantPrivilege
, a newUser
instance is copied from the original instance and the extra privilege is added using the case classcopy
constructor. Thecopy
constructor was discussed in the Case Classes lecture. There is a 30% chance thatgrantPrivilege
will fail.Scala codecase class User(name: String, privilege: List[String]) { /** simulate slow database access */ def grantPrivilege(newPriv: String) = Future { Thread.sleep(random.nextInt(1000)) if (System.currentTimeMillis % 3==0) throw new Exception("Unlucky time to grant privilege, not gonna do it!") copy(privilege = newPriv :: privilege) } }
object User { private val userMap = Map( 1L -> "Fred Flintstone", 2L -> "Wilma Flintstone", 3L -> "Barney Rubble", 4L -> "Betty Rubble" )
/** Simulate slow database access */ def findById(id: Long): Future[User] = Future { Thread.sleep(random.nextInt(1000)) User(userMap(id), Nil) } }Future.flatMap
has the same semantics as theflatMap
you already saw for Scala collections in the Combinators lecture. Inside the body of theflatMap
, the contents of the monad are available and can be transformed. In this case we callUser.grantPrivilege
, pass aString
describing the new privilege to be added to theUser
instance'sprivilege
property, and obtain aFuture[User]
. Ifmap
had been used, aFuture[Future[User]]
would have been produced; instead,flatMap
flattens the result to produce aFuture[User]
, which is then passed along to the firstandThen
callback.andThen
prints out the result, and the 2ndandThen
callback signals that the program should terminate. This signalling pattern was introduced in the Future Callbacks lecture.Scala codeval signal = Promise[String]() val user: Future[User] = User.findById(random.nextInt(4)+1) .flatMap { _.grantPrivilege("student") } .andThen { case Success(value) => println(s"""${value.name}'s privilege is now: ${value.privilege.mkString(", ")}.""") case Failure(throwable) => println(s"Problem augmenting student privilege: ${throwable.getMessage}") }.andThen { case _ => signal.success("All done") } Await.ready(signal.future, 30 minutes)
You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureFlatMap"
70% of the time output is something like:
OutputFred Flintstone's privilege is now: student.
30% of the time output is:
OutputProblem augmenting student privilege: Unlucky time to grant privilege, not gonna do it!
-
foreach
foreach[U](f: T ⇒ U)
(implicit executor: ExecutionContext): Unit -
Asynchronously processes the value in the
Future
once the value becomes available. This combinator is only useful for its side effects because it returnsUnit
. You might think thatforeach
is only useful when dealing with collections of futures, but insteadforeach
is a good way for an operation to be performed on aFuture
in a non-blocking, asynchronous manner.Scala codeFuture(factorial(12345)).foreach(println)
You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureForeach"
Output is:
Scala code34436424691867823916158... very long output ...
The code example in the
courseNotes
project uses aPromise
to signal completion, otherwise no output would be seen. -
map
map[S](f: T ⇒ S)
(implicit executor: ExecutionContext): Future[S] -
map
returns a newFuture
that contains the value of the originalFuture
transformed according to theFunction
passed in.map
is useful for applying a synchronous function in order to create a successfulFuture
.map
does not operate on aFuture
if it failed; instead,map
passes along failedFuture
s unchanged. The following maps aFuture[BigInt]
toFuture[Boolean]
; the value of the newFuture
istrue
iffactorial
returned an even number.Scala codeFuture(factorial(12345)).map { _ % 2 == 0 }
You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureMap"
The version of the program in the
courseNotes
project has been modified so it generates output before the main thread ends. -
mapTo
mapTo[S](implicit tag: ClassTag[S]): Future[S] -
mapTo
attempts to create aFuture
with a value type coerced to the given typeS
. If unsuccessful aClassCastException
is thrown.mapTo
is useful for downcasting aFuture
's value, for example fromAny
toInt
.mapTo
is often used with Akka. This method should rarely be used since it defeats type safety.This code example takes advantage of the fact that
Future
is covariant, so aFuture[Int]
can be cast toFuture[Any]
.mapTo
does not change theFuture
's value, it just changes the declared type.Scala codeval x: Future[Any] = Future.successful(1) val y: Future[Int] = x.mapTo[Int]
You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureMapTo"
-
transform
transform[S](s: T ⇒ S, f: Throwable ⇒ Throwable)
(implicit executor: ExecutionContext): Future[S] -
transform
is likemap
, except that in addition to theFunction
that transforms the value of a successfulFuture
it also accepts a method that transforms theThrowable
of a failedFuture
. This means that unlikemap
, which ignores failedFuture
s,transform
operates on allFuture
s. If you don't intend to change theThrowable
you should usemap
orcollect
instead oftransform
. You could think oftransform
with theidentity
transformation as 'transforming theThrowable
'. We learned about theidentity
transformation in the Combinators lecture.Here are two examples of how you can use transform on successful and failed
Future
s: the first example changes both the value of a successfulFuture
and theException
of a failedFuture
, while the second example preserves the success value by using theidentity
transform, and only changes theException
of the failedFuture
.Scala codeval f1 = Future.successful(random.nextInt(100)) .filter(_%2==0) .transform(_/2, throwable => new Exception("Allergic to odd numbers... achoo!", throwable)) .andThen { case Success(value) => println(s"The (even) random number divided by 2 = $value") }
val f2 = Future(6/0) .transform(identity, throwable => new Exception("Something went wrong.", throwable))
val signal = Promise[String]() Future.sequence(List(f1, f2)) .andThen { case Success(value) => println(value) case Failure(throwable) => println(throwable.getMessage) }.andThen { case _ => signal.success("All done") } Await.ready(signal.future, 30 minutes)You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureTransform"
50% of the time output is:
OutputAllergic to odd numbers... achoo!
The other 50% of the time output is something like:
OutputThe (even) random number divided by 2 = 7 Something went wrong.
-
zip
zip[U](that: Future[U]): Future[(T, U)] -
zip
is useful for associating aFuture
with an index or any arbitrary key. This combinator works with asychronous methods that return aFuture
. notice how similar this method is toflatMap
;zip
accepts aFuture
, whereasflatMap
accepts a method that returns aFuture
.This code example simulates a lottery, where users are retrieved from a (slow) database by specifying their
id
, and lucky numbers are (slowly) generated, using a database which ensures uniqueness. Thezip
method will create aFuture[(User, Int)]
, where theInt
is the lucky number.Scala codecase class User(name: String, id: Long)
def getUser: Future[User] = Future { // simulate slow database access Thread.sleep(random.nextInt(1000)) User("Fred Flintstone", 123) }
def lotteryNumber: Future[Int] = Future { // simulate slow database access Thread.sleep(random.nextInt(1000)) random.nextInt(1000000) }
getUser zip lotteryNumber andThen { case Success(tuple) => println(s"User ${tuple._1.name} with id ${tuple._1.id} has lucky number ${tuple._2}.") case Failure(throwable) => println("Problem: " + throwable.getMessage) } andThen { case _ => signal.success("All done") }You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureZip"
Typical output is:
OutputUser Fred Flintstone with id 123 has lucky number 125876.
The version of the program in the
courseNotes
project has been modified so it generates output before the main thread ends.
Combinators for Collections of Futures
These methods are provided by the Future
companion object.
Some of them are similar to methods of the same name provided by the
Iterable
companion object, but they are non-blocking.
The Scala compiler ensures that you won't mix up the methods provided by Future
and Iterable
, however.
-
find
find[T](futures: Iterable[Future[T]])
(p: (T) => Boolean)
(implicit executor: ExecutionContext): Future[Option[T]] -
Completes with a result that satisfies the predicate;
None
is returned if noFuture
satisfies the predicate.Normally, Scala
Future
s always run to completion; invoking thefind
combinator does not change that. If you need to optimize your program's use ofFuture
s, see the Canceling Futures section in the Working With Collections of Futures lecture.Notice that the
andThen
combinator has three cases: two forSuccess
and one forNone
.Scala codeval f1 = Future(factorial(12345)) val f2 = Future(factorial(23456)) val f3 = Future(factorial(34567)) val signal = Promise[String]() Future .find(List(f1, f2, f3)) { _ % 2 == 0 } .andThen { case Success(Some(result)) => println(s"result = $result") case Success(None) => println(s"No result was even") case Failure(throwable) => println(throwable.getMessage) }.andThen { case _ => signal.success("All done") } Await.ready(signal.future, 30 minutes)
You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureFind" result = 3443642469186... // very long number
-
firstCompletedOf
firstCompletedOf[T](futures: IterableOnce[Future[T]])
(implicit executor: ExecutionContext): Future[T] -
Section to complete.
All
Future
s run to completion; ScalaFuture
has no provision for terminating aFuture
once it starts, which is truly unfortunate. Twitter Futures (Finangle) do not have this limitation. The Working With Collections of Futures lecture shows a way of overcoming this limitation.Scala codeval f1 = Future(factorial(12345)) val f2 = Future(factorial(23456)) val f3 = Future(factorial(34567)) Future.firstCompletedOf(List(f1, f2, f3)).andThen { case Success(result) => println(s"result = $result") case Failure(throwable) => println(throwable.getMessage) }
You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureFirstCompletedOf"
-
foldLeft
foldLeft[T, R](futures: IterableOnce[Future[T]])
(zero: R)
(op: (R, T) ⇒ R)
(implicit executor: ExecutionContext): Future[R] -
Prior to Scala 2.13 this method was called
fold
.foldLeft
iterates over the specifiedFuture
s and accumulates a result, using the given starting valuezero
. The computation of the result is performed on the thread used by the lastFuture
to complete. The result will be the first failure of any offutures
, or any failure encountered byop
, or the result of thefoldLeft
.Two examples of different
foldLeft
s are shown below. The firstfoldLeft
uses a lambda function (more precisely, aFunction2[BigInt, BigInt, BigInt]
) to create a newFuture
containing the summed value of all theFuture
s passed to it. The secondfoldLeft
uses a namedFunction2[BigInt, BigInt, BigInt]
calledbigMax
to create a newFuture
containing the largest value of all theFuture
s passed to it.The program is written in such a way that the second
foldLeft
does not run until the firstfoldLeft
has completed, however there is no reason the program could not be written such that bothfoldLeft
s occurred simultaneously since the computations are independent. The program shuts down after the secondfoldLeft
.Scala codeval f1 = Future(factorial(12345)) val f2 = Future(factorial(23456)) val f3 = Future(factorial(34567)) val futures = List(f1, f2, f3)
Future.foldLeft(futures)(BigInt(0))(_ + _) andThen { case Success(result) => println(s"foldLeft addition result = $result") case Failure(throwable) => println(throwable.getMessage) } andThen { case _ => val bigMax: (BigInt, BigInt) => BigInt = (x: BigInt, y: BigInt) => if (x>y) x else y Future .foldLeft(futures)(BigInt(0))(bigMax) .andThen { case Success(result) => println(s"foldLeft max result = $result") case Failure(throwable) => println(throwable.getMessage) } }You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureFold" foldLeft addition resu lt = 21364444 ... // very big number foldLeft max result = 21364444 ... // very big number
-
reduceLeft
reduceLeft[T, R >: T](futures: collection.immutable.Iterable[Future[T]])
(op: (R, T) => R)
(implicit executor: ExecutionContext): Future[R] -
reduceLeft
is likefoldLeft
, except that thezero
value is taken from the value of the firstFuture
to complete, instead of being explicitly specified. Since this is normally what you want,reduceLeft
is used more frequently thanfoldLeft
.This code example is just like the code example for
foldLeft
, with thefoldLeft
'szero
parameter removed since it is not needed forreduceLeft
.Scala codeval f1 = Future(factorial(12345)) val f2 = Future(factorial(23456)) val f3 = Future(factorial(34567)) val futures = List(f1, f2, f3) val bigMax = (x: BigInt, y: BigInt) => if (x>y) x else y val signal = Promise[String]()
Future.reduceLeft(futures)(_+_) .andThen { case Success(result) => println(s"Reduce addition result = $result") case Failure(throwable) => println(throwable.getMessage) } andThen { case _ => Future.reduceLeft(futures)(bigMax) .andThen { case Success(result) => println(s"Reduce max result = $result") case Failure(throwable) => println(throwable.getMessage) }.andThen { case _ => signal.success("All done") } } Await.ready(signal.future, 30 minutes)You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureReduce" reduce addition result = 2136444 ... // very big number reduce max result = 21364444 ... // very big number
-
sequence
sequence[A, CC[X] <: IterableOnce[X], To](in: CC[Future[A]])
(implicit bf: BuildFrom[CC[Future[A]], A, To], executor: ExecutionContext): Future[To] -
sequence
takes anIterableOnce[Future[A]]
and returns an equivalentFuture[IterableOnce[A]]
. The newFuture
will complete when allFuture
s in the collection have completed. If anyFuture
in the collection fails, that failure will be propagated to the newFuture
, however allFuture
s will run to completion even if one or moreFuture
s should fail.For example,
sequence
can be used to transform aList[Future[BigInt]]
into aFuture[List[BigInt]]
, so a callback can be triggered after all the originalFuture
s have completed.Scala codeval f1 = Future(factorial(12345)) val f2 = Future(factorial(23456)) val f3 = Future(factorial(34567)) val futures = List(f1, f2, f3) val signal = Promise[String]()
Future.sequence(futures) .andThen { case Success(result) => println(s"Sequence result = $result") case Failure(throwable) => println(throwable.getMessage) }.andThen { case _ => signal.success("All done") } Await.ready(signal.future, 30 minutes)You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureSequence" Reduce result = List(3443642469... very big number ...)
-
traverse
traverse[A, B, M[X] <: IterableOnce[X]](in: M[A])
(fn: (A) => Future[B])
(implicit bf: BuildFrom[M[A], B, M[B]], executor: ExecutionContext): Future[M[B]] -
traverse
transforms anIterableOnce[A]
into aFuture[IterableOnce[B]]
using the givenFunction1
[A] => Future[B]
.traverse
is useful for performing a parallel and asynchronousmap
and is more flexible thansequence
. The resultingFuture
only completes after all theFuture
s in the passed in collection complete. The order of the elements in the collection of the resultingFuture
's value will correspond to the order of the elements of the original collection. If any of theFuture
s fail, the resultingFuture
will contain theThrowable
of the first failedFuture
, however allFuture
s will run to completion even if one or more fail.For example, the following asynchronously applies
factorial
to all items oflist
in parallel, then prints a summary of all of the completedFuture
s.Scala codeval signal = Promise[String]() Future.traverse(List(12345, 234, 345)) { x => Future(factorial(x)) andThen { case Success(value) => println(s"""factorial($x)=$value""") } }.andThen { case Success(list) => println(list.mkString("traverse results:\n ", "\n ", "")) case Failure(throwable) => println(throwable.getMessage) }.andThen { case _ => signal.success("All done") } Await.ready(signal.future, 30 minutes)
You can run this program by typing:
Shell$ sbt "runMain multi.futures.FutureTraverse"
The output shows that each
Future[BigInt]
completes asynchronously, however the resultingFuture[List[BigInt]
only completes after everyFuture[BigInt]
has completed.Outputfactorial(234)=22670150 ... // very big number factorial(345)=24215638 ... // very big number factorial(12345)=35135103 ... // very big number traverse results: 35135103 ... // very big number 22670150 ... // very big number 24215638 ... // very big number
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.