Last active
June 1, 2023 20:40
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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