qed qed - 1 month ago 15
Scala Question

How can you make `first` return a `Failure` if both f and g fail?

An example from the official scala documentation:

def first[T](f: Future[T], g: Future[T]): Future[T] = {
val p = promise[T]
f onSuccess {
case x => p.trySuccess(x)
}
g onSuccess {
case x => p.trySuccess(x)
}
p.future
}



Note that in this implementation, if neither f nor g succeeds, then
first(f, g) never completes (either with a value or with an
exception).


It gives us a warning, but no corresponding solution. How can you make
first
return a
Failure
if both f and g fail?

Answer

Another solution: where you need to find the first successful one amongst a list. Or a failure if all fails.

The solution is a bit effectful by nature, would be happy to see pure functional implementation:

def second[T](ls: Seq[Future[T]]): Future[T] = {
  val p = Promise[T]()
  val size = ls.size
  val count = new AtomicInteger(0)

  ls.foreach(x => x onComplete{
    case Success(a) => p.tryCompleteWith(x)
    case Failure(y) =>  if(count.incrementAndGet() == size) p.tryCompleteWith(x)
  })
  p.future
}

The below prints 2 as expected:

second(List(Future{ Thread.sleep(5000); 1}, Future{Thread.sleep(3000) ;2}, Future{throw new RuntimeException})) onComplete{
  case Success(x) => println(x)
  case Failure(y) => y.printStackTrace()
}