Race two computations

A number of computations can be raced with each other using the raceSuccess method, for example:

import ox.{raceSuccess, sleep}
import scala.concurrent.duration.*

def computation1: Int =
  sleep(2.seconds)
  1

def computation2: Int =
  sleep(1.second)
  2

val result: Int = raceSuccess(computation1, computation2)
// 2

The losing computation is interrupted. raceSuccess waits until both branches finish; this also applies to the losing one, which might take a while to clean up after interruption.

It is also possible to race a sequence of computations, given as Seq[() => T].

Race variants

  • raceSuccess returns the first success, or if all fail, re-throws the first exception

  • raceResult returns the first success, or if any fail, re-throws the first exception (the first computation which finishes in any way is the “winner”)

Using application errors

Some values might be considered as application errors. If a computation returns such an error, raceSuccess continues waiting if there are other computations in progress, same as when an exception is thrown. Ultimately, if no result is successful, raceSuccess either throws the first exception, or the first application error that has been reported (whichever comes first).

It’s possible to use an arbitrary error mode by providing it as the initial argument to raceSuccess. Alternatively, a built-in version using Either is available as raceEither:

import ox.{raceEither, sleep}
import scala.concurrent.duration.*

raceEither({
  sleep(200.millis)
  Left(-1)
}, {
  sleep(500.millis)
  Right("ok")
}, {
  sleep(1.second)
  Right("also ok")
})

Here, the example returns Right("ok"); the first result is considered an error (a Left), and the third computation is cancelled.