Promises in Scala
In this lesson, you will learn about Promises in Scala. Promises are closely related to Futures and give you explicit control over when and how a Future is completed.
While a Future represents a result that will arrive later, a Promise is the object that produces that result.
Future vs Promise
Understanding the difference between Futures and Promises is essential.
- Future – read-only view of an asynchronous result
- Promise – writable handle used to complete a Future
A Promise creates a Future, but only the Promise can complete it.
When Should You Use Promises?
Promises are useful when:
- You need to complete a Future manually
- The result depends on external events
- You are integrating callback-based APIs
- You want fine-grained control over async flow
Importing Promise
To use Promises, import them from the Scala concurrency package.
import scala.concurrent.Promise
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
Creating a Promise
You create a Promise using the Promise companion object.
val promise = Promise[Int]()
Each Promise is associated with a Future.
Getting the Future from a Promise
Every Promise exposes a Future using the future method.
val future = promise.future
Consumers interact with the Future, not the Promise.
Completing a Promise Successfully
To complete a Promise with a value, use success.
promise.success(100)
Once completed, the associated Future is also completed.
Completing a Promise with Failure
If an error occurs, complete the Promise with failure.
promise.failure(new RuntimeException("Something went wrong"))
The Future will now be in a failed state.
Observing the Future Result
You can attach callbacks to the Future created by a Promise.
future.onComplete {
case scala.util.Success(value) =>
println(s"Received: $value")
case scala.util.Failure(error) =>
println(s"Error: ${error.getMessage}")
}
Example: Promise with Background Task
This example completes a Promise after a background computation.
val promise = Promise[String]()
val future = promise.future
Future {
Thread.sleep(1000)
promise.success("Task completed")
}
future.foreach(println)
This pattern is useful for bridging synchronous and asynchronous code.
Completing a Promise Only Once
A Promise can be completed only once. Any further attempts will be ignored or throw an exception.
promise.success(10)
promise.success(20) // ignored or fails
Using trySuccess and tryFailure
To safely attempt completion without exceptions,
use trySuccess or tryFailure.
promise.trySuccess(50)
promise.tryFailure(new Exception("Fail"))
These methods return true or false.
Promises vs Callbacks
Promises help replace deeply nested callbacks with cleaner code.
- Better readability
- Composable with Futures
- Less error-prone
Common Use Cases
- Wrapping legacy async APIs
- Custom async workflows
- Manual Future completion
- Event-driven systems
📝 Practice Exercises
Exercise 1
Create a Promise that completes with a number after a delay.
Exercise 2
Complete a Promise with a failure and handle it.
Exercise 3
Use trySuccess to safely complete a Promise.
✅ Practice Answers
Answer 1
val p = Promise[Int]()
Future {
Thread.sleep(500)
p.success(99)
}
p.future.foreach(println)
Answer 2
val p = Promise[String]()
p.failure(new Exception("Error occurred"))
p.future.failed.foreach(println)
Answer 3
val p = Promise[Int]()
p.trySuccess(1)
What’s Next?
In the next lesson, you will learn about Akka Actors and how message-driven concurrency works in Scala.