Pritam Kadam
Pritam Kadam

Reputation: 2527

How to execute list of scala futures sequentially

I want to execute List of functions which returns futures sequentially.

What could be the possible implementations for following method:

def runSequentially[A, B](lazyFutures: List[A ⇒ Future[B]])(input: A)
(implicit ec: ExecututionContext): Future[List[B]]

Test

test("execute futures sequentially") {
    import scala.concurrent.ExecutionContext.Implicits.global

    def runSequentially[A, B](lazyFutures: List[A ⇒ Future[B]])(input: A): Future[List[B]] = ???

    val l: List[Unit ⇒ Future[Unit]] = List(
      _ ⇒ Future { Thread.sleep(1000); println(1) },
      _ ⇒ Future { Thread.sleep(5000); println(2) },
      _ ⇒ Future { Thread.sleep(500); println(3) }
    )

    Await.result(runSequentially(l)(5), 20.seconds)

  }

This should print:

1
2
3

Upvotes: 2

Views: 2300

Answers (4)

Mario Galic
Mario Galic

Reputation: 48400

Try creating a pool with one thread and use Future.traverse like so

implicit val singleThreadEc: ExecutionContext = 
  ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor())

def runSequentially[A, B](lazyFutures: List[A ⇒ Future[B]])(input: A): Future[List[B]] =
  Future.traverse(lazyFutures)(f => f(input))

Upvotes: 4

Bogdan Vakulenko
Bogdan Vakulenko

Reputation: 3390

You can also use cats and Kleisli

import scala.concurrent.ExecutionContext.Implicits.global
import cats.implicits._
import cats.data.Kleisli

def runSequentially[A, B](lazyFutures: List[A ⇒ Future[B]])(input: A) : Future[List[B]] = lazyFutures
    .map(Kleisli.apply) //List[Kleisli[Future, A, B]]
    .sequence           //Kleisli[Future, A, List[B]]
    .run(input)         

Upvotes: 2

Pritam Kadam
Pritam Kadam

Reputation: 2527

This is what I came up with which does work

def runSequentially[A, B](lazyFutures: List[A ⇒ Future[B]])(input: A): Future[List[B]] = {
      lazyFutures.foldLeft(Future.successful(List.empty[B])) { (acc, curr) ⇒
        for {
          a ← acc
          c ← curr(input)
        } yield c :: a
      }
    }

Upvotes: 0

Levi Ramsey
Levi Ramsey

Reputation: 20551

def runSequentially[A, B](lazyFutures: Seq[A => Future[B]])(input: A)(implicit ctx: ExecutionContext): Future[List[B]] =
  lazyFutures.foldLeft(Future.successful(List.empty[B])) { (futAcc, f) =>
    futAcc.flatMap { acc =>
      f(input).flatMap { result =>
        result :: acc
      }
    }
  }.map(_.reverse)

Should do the trick (haven't tested it). By prepending to the list and reversing, the complexity is O(n), while appending is O(n^2), since appending is O(n) and the append is done n times.

Upvotes: 2

Related Questions