Scala for Java developers

Lesson 7: Scala Futures

Quick recap

  • Error handling using Try
  • What are the two accepted types of a Try?

Quick recap

  • Error handling using Try
  • What are the two accepted types of a Try?
  • Success and Failure
def parseURL(url: String): Try[URL] = Try(new URL(url))

val result = parseURL("http://www.hotels.com")

result match {
  case Success(url) => println(url)
  case Failure(ex) => println(s"An error occurred: ${ex.getMessage}")
}

Why Scala Futures?

  • Well-behaved concurrent programs in an easy way
  • Asynchronous computations
  • Non-blocking executions
  • Parallel executions

Scala Futures

Future[T]

  • package scala.concurrent
  • Represents an eventual result of type T
    • This T value may or may not currently be available, but it will be available at some point
    • If something goes wrong or times out, it will contain an exception instead

https://blog.knoldus.com/2016/07/01/getting-asynchronous-in-scala-part-1-future-callbacks-combinators-etc/

Execution Context

object Future {
 def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] 
}
  • Execution context will allow to execute our future => Executor, like a ThreadPoolExecutor
  • You can use your own Execution context or the global one
  • ExecutionContext.global:
    • Execution context that will set, by default, threads to the amount of your available processors 
    • It should be sufficient for most situations but requires some care

Using Global Execution Context

import scala.concurrent.ExecutionContext.Implicits.global
  • Importing it at class level
  • Passing explicitly the execution context
Future { /* do something */ }(ExecutionContext.global)

Create a Future[T]

  • Future()

    • Creates a Future for an asynchronous computation

    • Uses the executor => body will be executed in a different thread

object Future {
 def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] 
}
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def getStudents(courseName: String): Future[List[String]] = Future {
  codeAcademyService.getStudents(courseName)
}

Create a Future[T]

object Future {
 def failed[T](exception: Throwable): Future[T]
 def successful[T](result: T): Future[T]
}
  • Future.failed
    •  Creates an already completed Future with the specified exception
  • Future.successful
    •  Creates an already completed Future with the specified value
Future.failed(new RuntimeException("I will fail"))
Future.successful("Successful value")

Create futures that doesn't need thread-switching

Callbacks

def onComplete[U](f: Try[T] => U)(implicit executor: ExecutionContext): Unit
import scala.util.{Failure, Success}

val eventualStudents = getStudents("Scala4JavaDevs")

eventualStudents.onComplete {
  case Success(students) => for(student <- students) println(student)
  case Failure(exception) => println(exception.getMessage)
}

Example

  • A callback is called asynchronously once the future is completed
  • Result of a Future is wrapped in to a Try[T]
  • Callbacks allow a non-blocking execution
  • Provides a better performance

Impatience? Get the result

import scala.concurrent.Await
import concurrent.duration._

Await.result(result, 1.second)
  • Blocking is not recommended in an asynchronous system
  • Sometimes is needed, for instance, for testing
def result[T](awaitable: Awaitable[T], atMost: Duration): T

Example

Composing Futures

def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S]
def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S]
getStudents("Scala4JavaDevs").map { students =>
  students.filter(name => name.startsWith("R"))
}

Examples

  • map, flatMap
    • Creates a new Future by applying a function to the successful result of this future
    • If this future is completed with an exception then the new future will also contain this exception
def companiesByEmployees(employeeNames: List[String]): Future[List[String]] = Future {
  employeeServiceApi.getCompanyNames(employeeNames)
}

getStudents("Scala4JavaDevs").flatMap { students =>
  companyByEmployee(students)
}

for-comprehension

  • Translate to flatMap but easier to read
  • Very useful when composing multiple dependent futures
for {
  students <- getStudents("Scala4JavaDevs")
  companies <- companiesByEmployees(students)
  companiesAddress <- asyncService.getCompanyAddressses(companies)
  // ... all sequential futures we want to do
} yield companiesAddress
  • Like a flatMap, it only passes to next Future once the previous Future is completed successfully

parallel calls

val eventualTopStudent: Future[String] = service.getTopStudent
val eventualTopCompany: Future[String] = service.getTopCompany

val result: Future[String] = 
    for {
      student <- eventualTopStudent
      company <- eventualTopCompany
    } yield s"Top student: $student, Top company: $company"
  • To parallelise different processes, we call the services
  • Then, we can also join the results

Example

sequential vs parallel

// Parallel code

def operation(op: String) = Future {
 Thread.sleep(400)
 println(s"$op operation")
}

val asyncTask1 = operation("First")
val asyncTask2 = operation("Second")

val allCalls: Future[Unit] = for {
   result1 <- asyncTask1
   result2 <- asyncTask2
} yield println("Finished")

Await.result(allCalls, 3.second)
// Sequential code

def operation(op: String) = Future {
 Thread.sleep(400)
 println(s"$op operation")
}

val allCalls: Future[Unit] = for {
   result1 <- operation("First")
   result2 <- operation("Second")
} yield println("Finished")

Await.result(allCalls, 3.second)
// Expected output
First operation
Second operation
Finished
// Expected output - Non-deterministic
// Output 1
First operation
Second operation
Finished

// Output 2
Second operation
First operation
Finished

Time for exercises!

cd path/to/scala-for-java-devs
git stash
git pull
git checkout lesson7

./sbt test

Your feedback is highly appreciated :-)

Scala for Java Developers. Lesson 7: Futures

By Sonia Fernández Rodríguez

Scala for Java Developers. Lesson 7: Futures

  • 105