Skip to content

Instantly share code, notes, and snippets.

@dportabella
Last active June 1, 2023 20:40
Show Gist options
  • Save dportabella/4e7569643ad693433ec6b86968f589b8 to your computer and use it in GitHub Desktop.
Save dportabella/4e7569643ad693433ec6b86968f589b8 to your computer and use it in GitHub Desktop.
Explanation on how to execute scala futures in serial one after the other
/*
Execute scala futures in serial one after the other
This gist is to explain the solution given in
http://www.michaelpollmeier.com/execute-scala-futures-in-serial-one-after-the-other-non-blocking
The three examples produce the same result:
---
done: 10
done: 20
done: 30
List(done: 10, done: 20, done: 30)
---
example1 uses the solution found on that page (a bit changed)
example2 uses a custom implementation of foldLeft to help understanding how it works
example3 shows the result of manually applying foldLeft on that particular input, and the solution should be easier to understand
note that in the previous page, the author also provides a more generic function of serialiseFutures.
*/
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future}
object ExampleExecuteScalaFuturesInSerial extends App {
def myFuture(i: Int) = Future {
Thread.sleep(1000)
val s = s"done: $i"
println(s)
s
}
def example1: Future[List[String]] = {
def serialiseFutures[A, B](list: Iterable[A])(fn: A => Future[B])(implicit ec: ExecutionContext): Future[List[B]] = {
val zero = Future(List.empty[B])
list.foldLeft(zero) { (accFutureList, nextItem) =>
accFutureList.flatMap(accResultList => {
val nextFuture = fn(nextItem)
nextFuture.map(nextResult => {
accResultList :+ nextResult
})
})
}
}
serialiseFutures(List(10, 20, 30)) { i => myFuture(i) }
}
def example2: Future[List[String]] = {
def foldLeft[A, B](list: List[A], zero: B, f: (B, A) => B) = {
def step(restList: List[A], acc: B): B = restList match {
case Nil => acc
case head :: tail => step(tail, f(acc, head))
}
step(list, zero)
}
def serialiseFutures[A, B](l: List[A])(fn: A => Future[B])(implicit ec: ExecutionContext): Future[List[B]] = {
val zero = Future(List.empty[B])
foldLeft(l, zero, (accFutureList: Future[List[B]], nextItem: A) => {
accFutureList.flatMap(accResultList => {
val nextFuture = fn(nextItem)
nextFuture.map(nextResult => {
accResultList :+ nextResult
})
})
})
}
serialiseFutures(List(10, 20, 30)) { i => myFuture(i) }
}
def example3: Future[List[String]] = {
val zero = Future(List.empty[String])
zero
.flatMap(accResultList => {
val nextFuture = myFuture(10)
nextFuture.map(nextResult => {
accResultList :+ nextResult
})
})
.flatMap(accResultList => {
val nextFuture = myFuture(20)
nextFuture.map(nextResult => {
accResultList :+ nextResult
})
})
.flatMap(accResultList => {
val nextFuture = myFuture(30)
nextFuture.map(nextResult => {
accResultList :+ nextResult
})
})
}
def run(futureListResult: Future[List[String]]) {
val listResult: List[String] = Await.result(futureListResult, Duration.Inf)
println(listResult)
}
run(example1)
run(example2)
run(example3)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment