Future
Scala's Future provides a powerful abstraction for working with asynchronous computations. It represents a value that may become available at some point, allowing your program to continue executing while waiting for the result. This is particularly useful for operations that involve IO, database queries, network requests, or any long-running computations.
Here's a simple example demonstrating how to use Scala Futures to perform asynchronous computations:
Setup
First, ensure you have the Scala execution context in scope, as it's needed to execute the futures:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
Basic Future Usage
The following example shows how to create a future, perform an operation asynchronously, and handle the result once it's available:
// Define a future that simulates a long-running computation
val futureComputation: Future[Int] = Future {
Thread.sleep(1000) // Simulating a long-running task
42 // The answer to the ultimate question of life, the universe, and everything
}
// Handling the future's result
futureComputation.onComplete {
case Success(result) => println(s"Result: $result")
case Failure(e) => println(s"An error occurred: ${e.getMessage}")
}
Working with Results
Often, you'll want to chain operations on futures or combine them. Here's how to use map, flatMap, and for comprehensions to work with future results:
val futureSquare: Future[Int] = futureComputation.map(x => x * x)
futureSquare.onComplete {
case Success(square) => println(s"Square of the result: $square")
case Failure(e) => println(s"Failed to calculate the square: ${e.getMessage}")
}
// Combining futures with for-comprehension
val anotherFutureComputation: Future[Int] = Future {
Thread.sleep(500) // Another simulated task
10
}
val combinedResult: Future[Int] = for {
result1 <- futureComputation
result2 <- anotherFutureComputation
} yield result1 + result2
combinedResult.onComplete {
case Success(sum) => println(s"Sum of results: $sum")
case Failure(e) => println(s"Failed to combine results: ${e.getMessage}")
}
Error Handling
Handling errors with futures can be done using recover or recoverWith methods:
val riskyFuture: Future[Int] = Future {
throw new RuntimeException("Something went wrong")
}
val recoveredFuture: Future[Int] = riskyFuture.recover {
case _: RuntimeException => -1
}
recoveredFuture.onComplete {
case Success(value) => println(s"Recovered value: $value")
case Failure(e) => println("This should not be printed if recovered properly")
}