Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sequentially combine arbitrary number of futures in Scala

I'm new to scala and I try to combine several Futures in scala 2.10RC3. The Futures should be executed in sequential order. In the document Scala SIP14 the method andThen is defined in order to execute Futures in sequential order. I used this method to combine several Futures (see example below). My expectation was that it prints 6 but actually the result is 0. What am I doing wrong here? I have two questions:

First, why is the result 0. Second, how can I combine several Futures, so that the execution of the second Future does not start before the first Future has been finished.

val intList = List(1, 2, 3)

val sumOfIntFuture = intList.foldLeft(Future { 0 }) {
 case (future, i) => future andThen {
  case Success(result) => result + i 
  case Failure(e) => println(e)
 }
}

sumOfIntFuture onSuccess { case x => println(x) }
like image 928
Chrisse Avatar asked Dec 12 '12 09:12

Chrisse


2 Answers

andThen is for side-effects. It allows you to specify some actions to do after future is completed and before it used for something else.

Use map:

scala> List(1, 2, 3).foldLeft(Future { 0 }) {
     |  case (future, i) => future map { _ + i }
     | } onSuccess { case x => println(x) }
6
like image 93
senia Avatar answered Nov 17 '22 00:11

senia


I like this generic approach:

trait FutureImplicits {

  class SeriallyPimp[T, V](futures: Seq[T]) {
    def serially(f: T => Future[V])(implicit ec: ExecutionContext): Future[Seq[V]] = {
      val buf = ListBuffer.empty[V]
      buf.sizeHint(futures.size)

      futures.foldLeft(Future.successful(buf)) { (previousFuture, next) =>
        for {
          previousResults <- previousFuture
          nextResult <- f(next)
        } yield previousResults += nextResult
      }
    }
  }

  implicit def toSeriallyPimp[T, V](xs: Seq[T]): SeriallyPimp[T, V] =
    new SeriallyPimp(xs)

}

Then mix-in the above trait and use it like this:

val elems: Seq[Elem] = ???
val save: Elem => Future[Result] = ???
val f: Future[Seq[Result]] = elems serially save

This code could be improved to preserve the input collection type. See this article for example.

like image 25
Tvaroh Avatar answered Nov 17 '22 00:11

Tvaroh