scalaintermediate

Futures and Async Programming

Use Scala Futures for async operations: map, flatMap, recover, sequence, and race patterns.

scala
import scala.concurrent.{Future, Promise, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.*
import scala.util.{Success, Failure}

def fetchUser(id: Int): Future[String] = Future {
  Thread.sleep(100)
  if id <= 0 then throw RuntimeException(s"Invalid id: $id")
  s"User-$id"
}

def fetchOrders(user: String): Future[List[String]] = Future {
  Thread.sleep(100)
  List(s"$user-order1", s"$user-order2")
}

@main def run(): Unit =
  // Basic future
  val future = Future {
    Thread.sleep(50)
    42
  }

  // Callbacks
  future.onComplete {
    case Success(v) => println(s"Got: $v")
    case Failure(e) => println(s"Failed: ${e.getMessage}")
  }

  // map and flatMap
  val doubled = future.map(_ * 2)
  val chained = fetchUser(1).flatMap(fetchOrders)

  // for-comprehension
  val workflow = for
    user   <- fetchUser(1)
    orders <- fetchOrders(user)
  yield (user, orders)

  val (user, orders) = Await.result(workflow, 5.seconds)
  println(s"$user: $orders")

  // Error recovery
  val recovered = fetchUser(-1)
    .recover { case e: RuntimeException => "default-user" }
  println(Await.result(recovered, 5.seconds))

  // recoverWith: return another Future
  val fallback = fetchUser(-1)
    .recoverWith { case _ => fetchUser(1) }
  println(Await.result(fallback, 5.seconds))

  // Parallel execution
  val f1 = fetchUser(1)
  val f2 = fetchUser(2)
  val f3 = fetchUser(3)
  val parallel = for
    u1 <- f1  // all started before for-comprehension
    u2 <- f2
    u3 <- f3
  yield List(u1, u2, u3)
  println(s"Parallel: ${Await.result(parallel, 5.seconds)}")

  // Future.sequence: List[Future] => Future[List]
  val futures = (1 to 5).map(fetchUser).toList
  val all = Future.sequence(futures)
  println(s"All: ${Await.result(all, 5.seconds)}")

  // firstCompletedOf: race
  val fast = Future { Thread.sleep(50); "fast" }
  val slow = Future { Thread.sleep(200); "slow" }
  val winner = Future.firstCompletedOf(List(fast, slow))
  println(s"Winner: ${Await.result(winner, 5.seconds)}")

  // Promise
  val promise = Promise[String]()
  Future {
    Thread.sleep(100)
    promise.success("Promise fulfilled")
  }
  println(Await.result(promise.future, 5.seconds))

  Thread.sleep(500) // let callbacks finish

Use Cases

  • Asynchronous API calls
  • Parallel task execution
  • Error recovery in async workflows

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.