Skip to main content

Running concurrent Try functions in Scala

  • Posted

Suppose you have a Scala function that returns Try[_]. How do you call it more than once at the same time?

I had to solve this problem in a test suite at work. We have a worker function that returns a Try, and the function contains some locking logic to ensure only one instance of it can run at a time. I wanted to test it by calling multiple instances of the function, and checking that only one succeeded.

If you have a function that returns a Future, you can call it multiple times in a sequence and collect the results:

def greetF(name: String): Future[_] = Future {
  Thread.sleep(Random.nextInt(1000))
  println(s"Hello $name")
}

val futures: Seq[Future[_]] =
  Seq("alice", "bob", "carol", "dave", "erin").map { greetF }

When you run this code, you’ll see the names printed in a different order each time. Each instance of greet is running concurrently, so the sleeps are all counting down together.

What if we take this code, and replace Future with Try?

def greetT(name: String): Try[_] = Try {
  Thread.sleep(Random.nextInt(1000))
  println(s"Hello $name")
}

val tries: Seq[Try[_]] =
  Seq("alice", "bob", "carol", "dave", "erin").map { greetT }

Now the names get printed in alphabetical order. The Trys run synchronously – each one has to finish before the next one can start. That’s not what we want!

My first thought was to try wrapping my Try calls in Future.fromTry:

val futures: Seq[Future[_]] =
  Seq("alice", "bob", "carol", "dave", "erin")
    .map { name => Future.fromTry(greetT(name)) }

But this still prints the names in the same order – because Future.fromTry wraps an already-completed Try, it waits for the result of the inner Try to complete.

The solution I found was to initiate a new Future, then flatmap over that to use the Future.fromTry:

val futures: Seq[Future[_]] =
  Seq("alice", "bob", "carol", "dave", "erin")
    .map { name =>
      Future.successful(()).flatMap { _ =>
        Future.fromTry(greetT(name))
      }
    }

Now I have five Futures running concurrently, each of which calls the Try function. It’s a bit ugly, but it does the trick.