qed
qed

Reputation: 23104

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?

Upvotes: 2

Views: 74

Answers (2)

mutantacule
mutantacule

Reputation: 7063

Something like this could do the job, although it's not based on the same logic as your example (this solution is wrong, check the Update)

def first[T](f: Future[T], g: Future[T]): Future[T] = {
  val p = Promise[T]
  p.completeWith(f.fallbackTo(g))
  p.future
}

If both fails, first holds the throwable coming from f.

Update (based on @Jatin's comment): My first solution is wrong, because if the f future never completes, then even if g does, first never completes either. This updated solution completes the Promise nondeterministically with the first completed value:

def first[T](f: Future[T], g: Future[T]): Future[T] = {
  val p = Promise[T]
  p.completeWith(Future.firstCompletedOf(Seq(f.fallbackTo(g),g.fallbackTo(f))))
  p.future
}

Upvotes: 3

Jatin
Jatin

Reputation: 31724

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()
}

Upvotes: 1

Related Questions