scala - Future Recursion Patterns/Future Chaining of arbitrary length -
scala - Future Recursion Patterns/Future Chaining of arbitrary length -
i'm curious best way recursively build chain of akka futures run sequentially, if dowork
phone call in future fails, future should retried 3 times, chain should fail if runs out of retry attempts. assuming dowork
calls pass returned future futchain
should complete.
object main extends app { val futchain = recurse(2) def recurse(param: int, retries: int = 3): future[string] { future { doworkthatmayfailreturningstring(param...) } recoverwith { case e => if (retries > 0) recurse(param, retries -1) else future.failed(e) } flatmap { strres => recurse(nextparam) //how should res previous fut passed? } } futchain oncomplete { case res => println(res) //should print strings } }
how can results collection? i.e. in illustration each string
homecoming dowork
function (i need somehow modify recurse
func homecoming futrue[list[string]]
should utilize recover
or recoverwith
? is ok phone call flatmap
chain these calls should create considerations tail recursion & stack overflows? would improve recursively build list of futures , cut down them?
you can implement retryable future
this:
def retry[t](f: => future[t])(n: int)(implicit e: executioncontext): future[t] = { n match { case if (i > 1) => f.recoverwith{ case t: throwable => retry(f)(n - 1)} case _ => f } }
this isn't optimized tail recursion, if intend on retrying few times, won't stack overflow (and imagine if it's failed first few, it's going maintain failing, anyway).
then chaining separately. if have finite number of functions chain together, each depending on previous (and reason want aggregate results) can utilize for
comprehensions (syntactic sugar flatmap
):
for { firstresult <- retry(future(dowork(param)))(3) secondresult <- retry(future(dowork(firstresult)))(3) thirdresult <- retry(future(dowork(secondresult)))(3) } yield list(firstresult, secondresult, thirdresult)
for arbitrarily long chains, can them in parallel using future.sequence
(futures
in akka library):
def dowork(param: string): string = ... val parameters: list[string] = list(...) val results: future[list[string]] = future.sequence(parameters.map(dowork(_)))
this unravel otherwise list[future[string]]
future[list[string]]
.
here's 1 way similar thing in sequence:
def sequential[a, b](seq: list[a])(f: => future[b])(implicit e: executioncontext): future[list[b]] = { seq.foldleft(future.successful(list[b]())) { case (left, next) => left.flatmap(list => f(next).map(_ :: list)) } } def dowork(param: string): string = ... val results: future[list[string]] = sequential(parameters)(param => future(dowork(param)))
the implementation of these functions very sensitive utilize case. 2 above functions homecoming failed futures if of futures in chain failed. you'll want this, other times not. if want collect successful futures, , discard failed ones without failing entire result, can add together step recover failures.
additionally, difference between recover
, recoverwith
type of partialfunction
accepts. recover
replaces failed futures default values, while recoverwith
using future
. in case of retry
, recoverwith
more appropriate because i'm trying recover failed future
itself.
scala recursion akka future
Comments
Post a Comment