Suppose I've got a function foo
that performs asynchronous computation and returns a Future
:
def foo(x: Int)(implicit ec: ExecutionContext): Future[Int] = ???
Now I'd like to retry this computation n
times until the computation succeeds.
def retryFoo(x: Int, n: Int)(implicit ec: ExecutionContext): Future[Int] = ???
I would like also to return all exceptions thrown while retrying. So, I define new exception class ExceptionsList
and require retryFoo
to return ExceptionsList
when all retries fail.
case class ExceptionsList(es: List[Exception]) extends Exception { ... }
How would you write retryFoo
to retry foo
and return a Future
with either the foo
result or ExceptionsList
?
You can use Future's
transformWith
and a little recursion to do the retrying. UsingtransformWith
you can pattern match on the success or failure of your future. In the success case, you just return the result of theSuccess
and you're done since you don't care about intermediate failures. On the failure case you make a recursive call where you decrement the retry count and append the exception to a list in your parameter list to keep track of all your failures. Ifn
is ever zero, you've exhausted your retries. In this case you just create a new instance ofExceptionsList
and fail the future.The below example demonstrates the failure case when all futures throw exceptions. You can call
retryFoo(3)
and you will get a failed future with three exceptions.I'd probably do something like this:
expr
is call-by-name since it itself could throw an exception rather than returning a failed Future. I keep the accumulated exceptions in a list, but I guess that's a taste-thing.