Reputation: 23104
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
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
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