I recently thought of a fun problem. What if you wanted to build a way to retry certain asynchronous calls (e.g., long polling a url for an eventually there asset or retrying an HTTP call). I was fiddling around and came up with, what I think, is a pretty cool solution.

I know there are libraries that do this but building is fun and good for my brain!

Here is the solution I came up with a bit ago. I am using Promises to handle the asynchronous nature of this problem.

This is using Bluebird.js for the reflect API. We use recursion to retry until the slated number of attempts has been reached. Promises start running as soon as they are instantiated. Usually, when using Promises , we would start the function and then we can pass that Promise around attaching things as necessary. Unfortunately, if we want to retry the promise, we need to have the function and the args so we can restart it when needed. Thats why we need to pass in the async function and the args separately.

The Bluebird reflect() function takes a promise and wraps it in a Promise Inspection . The Promise Inspection mimics a resolved Promise but also reveals some useful methods. Namely, .isFulfilled() and .value() . We can use these methods to inspect the inner Promise . If the inner Promise fulfilled then we return the value. If it didn’t fulfill, then we push the error to an array and recurse with 1 less retries available.

Here it is in action:

A little later, I was working on a project using Flutures and needed a similar solution so I cobbled together this bit of code:

Futures (as implemented by Fluture ) have some interesting properties. One of the specific ones that makes it different than promises is that Futures don't execute until you attach a .fork call. Looking at the signature this function takes a backoff strategy function, a number of retries and a Future .

You’ll note, the retry function takes a full Future and not a function and args. This is because of the laziness of Futures . This means that we can wrap the function and the args up together and run it when we are ready. But, before we run it, we want to set up those retries.

For each retry we would want to do we add a chainRej() call. So if we are to retry 2 times then we do something like task.chainRej(...).chainRej(...) . We use recursion to accomplish this. The function we passed to chainRej is only invoked if the Future rejects (fails). When that happens we add the error to a list, we increment our retry counter and then we do our decisioning. If we have retried the maximum number of times then we reject the whole Future . If we haven’t used all our retries then we use the backoff function and Future.after() to pass the new number of attempts back to our recursing function.

One of the cool things about this one is that there is a way to pass in a backoff strategy. So we could use the same retry function a bunch of different ways. We could create a linear or exponential backoff and pass either of those into the retry function.

It solves the same problem in a whole new way. The only commonality in the two solutions is that we used recursion.

Here is the usage for that function:

In both cases it was fun to build some tools needed to solve this problem instead of just downloading another library. I find that these act as mental katas, helping me to understand the tools I’m using. Going through these motions also helps me feel more confident solving tough problems.