
- Scala - Home
- Scala - Overview
- Scala - Features
- Scala - Environment Setup
- Scala - Build Tool (SBT)
- Scala - REPL
- Scala - Dot & Dotty
- Scala - Basic Syntax
- Scala - Hello World Program
- Scala - Identifiers
- Scala - Keywords
- Scala - Comments
- Scala - Code Blocks
- Scala - Semicolon
- Scala - Constructs
- Scala - Expressions
- Scala - Input and Output
- Scala - Optional Braces
- Scala - Underscore (_)
- Data Types and Variables
- Scala - Data Types
- Scala - Type Bounds
- Scala - Context Bound
- Scala - Variances
- Scala - Type Hierarchy
- Scala - Variables
- Scala - Variable Scopes
- Scala - Literals
- Scala - Numeric Types
- Scala - Boolean Types
- Scala - Char Type
- Scala - Unit Types
- Scala - Strings
- Scala - Arrays
- Scala - Null Type
- Scala - Nothing
- Scala - Any Type
- Scala - AnyRef Type
- Scala - Unified Types
- Scala - Dates and Times
- Scala - Ranges
- Scala - Multidimensional Arrays
- Scala - WrappedArray
- Scala - StringBuilder
- Scala - String Interpolation
- Scala - StringContext
- Scala - Type Casting
- Scala var vs val
- Scala Operators
- Scala - Operators
- Scala - Rules for Operators
- Scala - Arithmetic Operators
- Scala - Relational Operators
- Scala - Logical Operators
- Scala - Bitwise Operators
- Scala - Assignment Operators
- Scala - Operators Precedence
- Scala - Symbolic Operators
- Scala - Range Operator
- Scala - String Concatenation Operator
- Scala Conditional Statements
- Scala - IF ELSE
- Scala - IF-ELSE-IF-ELSE Statement
- Scala - Nested IF-ELSE Statement
- Scala Loop Statements
- Scala - Loop Statements
- Scala - while Loop
- Scala - do-while Loop
- Scala - Nested Loops
- Scala - for Loop
- Scala - break Statement
- Scala - yield Keyword
- Scala Classes & Objects
- Scala - Classes & Objects
- Scala - Constructors
- Scala - Auxiliary Constructor
- Scala - Primary Constructor
- Scala - This Keyword
- Scala - Nested Classes
- Scala - Getters and Setters
- Scala - Object Private Fields
- Scala - Singleton Object
- Scala - Companion Objects
- Scala - Creating Executable Programs
- Scala - Stateful Object
- Scala - Enumerations
- Scala - Polymorphism
- Scala - Access Modifiers
- Scala - Apply Method
- Scala - Update Methods
- Scala - UnapplySeq Method
- Scala - Inheritance
- Scala - Extending a Class
- Scala - Method Overloading
- Scala - Method Overriding
- Scala - Generic Classes
- Scala - Generic Functions
- Scala - Superclass Construction
- Scala Methods & Functions
- Scala - Functions
- Scala - Main Methods
- Scala - Functions Call-by-Name
- Scala - Functions with Named Arguments
- Scala - Function with Variable Arguments
- Scala - Recursion Functions
- Scala - Default Parameter Values
- Scala - Functions without Parameters
- Scala - Implicit Parameters
- Scala - Higher-Order Functions
- Scala - Nested Functions
- Scala - Extension Methods
- Scala - Anonymous Functions
- Partially Applied Functions
- Scala - Lazy Val
- Scala - Pure Function
- Scala - Currying Functions
- Scala - Control Abstractions
- Scala - Corecursion
- Scala - Unfold
- Scala - Tail Recursion
- Scala - Infinite Sequences
- Scala - Dynamic Invocation
- Scala - Lambda Expressions
- Scala Collections
- Scala - Collections
- Mutable and Immutable Collections
- Scala - Lists
- Scala - Sets
- Scala - Maps
- Scala - TreeMap
- Scala - SortedMap
- Scala - Tuples
- Scala - Iterators
- Scala - Options
- Scala - Infinite Streams
- Scala - Parallel Collections
- Scala - Algebraic Data Types
- Scala Pattern Matching
- Scala - Pattern Matching
- Scala - Type Patterns
- Scala - Exception Handling
- Scala - Extractors
- Scala - Regular Expressions
- Scala Files I/O
- Scala - Files I/O
- Scala Advanced Concepts
- Scala - Closures
- Scala - Futures
- Scala - Promises
- Scala - Traits
- Scala - Trait Mixins
- Scala - Layered Traits
- Scala - Trait Linearization
- Scala - Sealed Traits
- Scala - Transparent Traits
- Scala - Literal Type Arithmetic
- Scala - Inline keyword
- Scala - Def, Var & Val
- Scala - Dropped Features
- Scala - BDD Testing
Scala - Futures
Futures are used to handle asynchronous computations in Scala. You can perform many operations concurrently in an efficient and non-blocking manner. You can write code that runs in parallel. It improves performance and responsiveness of your applications.
Future in Scala
Future represents a value that may not yet exist but will be available at some point in the future. Futures are placeholders for results of asynchronous computations. These can either succeed or fail. When the computation completes, the future is either completed successfully with a value or failed with an exception.
Futures are non-blocking and utilize callbacks and combinators for asynchronous programming. Following is the example which shows you how to create a future -
import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global val future: Future[String] = Future { "Hello, World!" } future.foreach(result => println(result))
Save the above program in Demo.scala. The following commands are used to compile and execute this program:
Command
\>scalac Demo.scala \>scala Demo
Output
Hello, World!
Creating Futures
To create a future, you need to use the Future object and pass a block of code to it. The code inside the block runs asynchronously. Following is the example which shows you how to create Future in Scala -
import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global def longRunningComputation(): Int = { Thread.sleep(3000) 42 } val futureResult: Future[Int] = Future { longRunningComputation() }
Execution Context
Futures require an ExecutionContext to run. The ExecutionContext manages the threads on which the futures execute. You can use the global execution context and create your own -
import scala.concurrent.ExecutionContext import java.util.concurrent.Executors val executorService = Executors.newFixedThreadPool(4) implicit val customExecutionContext: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
Handling Results
You can use callbacks like onComplete, foreach, and map. You can handle the result of the future. You can define what should happen when the future completes using these callbacks -
import scala.util.{Success, Failure} futureResult.onComplete { case Success(value) => println(s"Result: $value") case Failure(exception) => println(s"Error: ${exception.getMessage}") }
The foreach method only handles successful results -
futureResult.foreach(result => println(s"Result: $result"))
You can also use combinators like map and flatMap. These are used to transform the result of a future -
val doubledResult: Future[Int] = futureResult.map(_ * 2) doubledResult.foreach(result => println(s"Doubled Result: $result"))
Save the above program in Demo.scala. The following commands are used to compile and execute this program:
Command
\>scalac Demo.scala \>scala Demo
Output
Doubled Result: 84
Composing Futures
You can compromise Futures to handle more asynchronous workflows. You can chain multiple futures together using flatMap and for-comprehensions -
import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global val future1 = Future { 1 } val future2 = Future { 2 } val combinedFuture = for { result1 <- future1 result2 <- future2 } yield result1 + result2 combinedFuture.foreach(result => println(s"Combined Result: $result"))
Save the above program in Demo.scala. The following commands are used to compile and execute this program:
Command
\>scalac Demo.scala \>scala Demo
Output
Combined Result: 3
Error Handling
Error handling is important when you work with futures. Futures can fail, so you need to handle these failures. You can use methods like recover and recoverWith to handle errors -
import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global val futureWithError: Future[Int] = Future { throw new RuntimeException("Failed!") } val recoveredFuture = futureWithError.recover { case _: RuntimeException => 0 } recoveredFuture.foreach(result => println(s"Recovered Result: $result"))
You can use recover method to provide a default value in case of an error. Whereas, recoverWith method is an alternative future -
val alternativeFuture = futureWithError.recoverWith { case _: RuntimeException => Future.successful(0) }
Save the above program in Demo.scala. The following commands are used to compile and execute this program:
Command
\>scalac Demo.scala \>scala Demo
Output
Recovered Result: 0
Waiting for Futures
You may need to wait for a future to complete in some cases. Blocking is generally discouraged. You can use Await to block and wait for a future result -
import scala.concurrent.Await import scala.concurrent.duration._ val result = Await.result(futureResult, 5.seconds) println(s"Result: $result")
Save the above program in Demo.scala. The following commands are used to compile and execute this program:
Command
\>scalac Demo.scala \>scala Demo
Output
Result: 42
You can use blocking with caution. It can lead to performance issues and deadlocks.
Advanced Features
Promises
Promise is a writable and single-assignment container. Promise completes a future. You can use promises to create futures and complete these manually -
import scala.concurrent.Promise val promise = Promise[Int]() val future = promise.future promise.success(42) // Completes the future with a value
You can use Promises for more control over the completion of futures.
Combining Multiple Futures
You can combine multiple futures using methods like zip, traverse, and sequence -
import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global val future1 = Future { 1 } val future2 = Future { 2 } val combined = future1.zip(future2) combined.foreach { case (result1, result2) => println(s"Combined: $result1, $result2") } val listOfFutures = List(Future { 1 }, Future { 2 }, Future { 3 }) val futureOfList = Future.sequence(listOfFutures) futureOfList.foreach(resultList => println(s"List of results: $resultList"))
Save the above program in Demo.scala. The following commands are used to compile and execute this program:
Command
\>scalac Demo.scala \>scala Demo
Output
Combined: 1, 2 List of results: List(1, 2, 3)
Transforming Futures
There are various methods in Futures. These methods are used to transform their results, like map, flatMap, filter, collect, etc.. You can apply transformations and handle different cases -
val transformedFuture = futureResult.collect { case result if result > 0 => result * 2 }
Save the above program in Demo.scala. The following commands are used to compile and execute this program:
Command
\>scalac Demo.scala \>scala Demo
Output
84
Notes
- Futures handle asynchronous operations for non-blocking execution.
- Future represents a value that will be available in the future. It succeeds with a value or fails with an exception.
- Futures require an ExecutionContext to manage threads. It uses either the global context or a custom one.
- You can use onComplete, foreach, and map callbacks to handle future results.
- You can manage failures with recover and recoverWith methods to handle errors.
- You can chain futures using flatMap and for-comprehensions for complex workflows.
- You can use Promise to manually complete futures. It has more control over their completion.