Todo App
Creating a Todo application that leverages Scala's Futures and Promises involves handling tasks asynchronously. This is particularly useful for operations that might take a long time, such as database interactions. In this simplified example, we'll implement a basic Todo app with asynchronous add and get functionalities.
Step 1: Setting Up the Environment
First, ensure you have the Scala execution context in scope for running your futures:
import scala.concurrent.{Future, ExecutionContext, Promise}
import scala.util.{Success, Failure}
import scala.concurrent.ExecutionContext.Implicits.global
Step 2: Define the Todo Model and Storage
Define a simple Todo model and a mock asynchronous storage for todos. In a real application, this storage might interact with a database.
case class Todo(id: Int, task: String, isCompleted: Boolean)
object TodoStorage {
private var todos: List[Todo] = List()
// Asynchronous add method
def add(todo: Todo): Future[Unit] = Future {
Thread.sleep(1000) // Simulate database delay
todos = todo :: todos
}
// Asynchronous get method
def get(id: Int): Future[Option[Todo]] = Future {
Thread.sleep(1000) // Simulate database delay
todos.find(_.id == id)
}
}
Step 3: Implement the TodoService
The TodoService will interact with the TodoStorage. It includes error handling and demonstrates how to use a Promise to create a custom future that might be completed outside the usual flow.
object TodoService {
def addTask(task: String): Future[Int] = {
val todo = Todo(
id = TodoStorage.todos.length + 1, // Simple ID generation strategy
task = task,
isCompleted = false
)
for {
_ <- TodoStorage.add(todo)
} yield todo.id
}
def completeTask(id: Int): Future[Boolean] = {
val promise = Promise[Boolean]()
TodoStorage.get(id).onComplete {
case Success(Some(todo)) =>
TodoStorage.add(todo.copy(isCompleted = true)).onComplete {
case Success(_) => promise.success(true)
case Failure(_) => promise.failure(new Exception("Failed to update the todo."))
}
case Success(None) => promise.failure(new NoSuchElementException("Todo not found."))
case Failure(exception) => promise.failure(exception)
}
promise.future
}
}
Step 4: Usage Example
Here’s how you might use TodoService to add and complete tasks:
val added = TodoService.addTask("Learn Scala Futures and Promises")
added.onComplete {
case Success(id) =>
println(s"Added todo with id: $id")
TodoService.completeTask(id).onComplete {
case Success(_) => println(s"Todo $id completed.")
case Failure(exception) => println(s"Error completing todo: ${exception.getMessage}")
}
case Failure(exception) => println(s"Error adding todo: ${exception.getMessage}")
}