Skip to content

Instantly share code, notes, and snippets.

@domenic
Last active September 16, 2023 02:43
Show Gist options
  • Save domenic/2936696 to your computer and use it in GitHub Desktop.
Save domenic/2936696 to your computer and use it in GitHub Desktop.
Generalized promise retryer
"use strict";
// `f` is assumed to sporadically fail with `TemporaryNetworkError` instances.
// If one of those happens, we want to retry until it doesn't.
// If `f` fails with something else, then we should re-throw: we don't know how to handle that, and it's a
// sign something went wrong. Since `f` is a good promise-returning function, it only ever fulfills or rejects;
// it has no synchronous behavior (e.g. throwing).
function dontGiveUp(f) {
return f().then(
undefined, // pass through success
function (err) {
if (err instanceof TemporaryNetworkError) {
return dontGiveUp(f); // recurse
}
throw err; // rethrow
}
});
}
// Analogous synchronous code:
function dontGiveUpSync(fSync) {
try {
return fSync();
} catch (err) {
if (err instanceof TemporaryNetworkError) {
return dontGiveUpSync(fSync);
}
throw err;
}
}
// Note how we created this powerful abstraction without ever using ANY aspect of the promise implementation
// besides the Promises/A "thenable"-ness. In particular, we had NO need of a deferred library, and we can
// interoperate with ANY promise library, returning to the caller the same type of promise we received (i.e.
// we don't assimilate When.js promises into Q promises in order to work with them).
// An example of a library that makes extensive use of this property is
// https://github.com/domenic/chai-as-promised/
// Unfortunately such composable patterns are impossible with jQuery promises, since there is no way to trap
// and rethrow errors. jQuery only allows you to catch explicit rejections, but does not allow access to
// thrown exceptions, and furthermore you cannot transition into a rejected state by (re)throwing or out
// of one by choosing not to rethrow: in a jQuery promise's fulfilled and rejected callbacks, you need to
// manually use jQuery's deferred library to transition state.
@icodeforlove
Copy link

I find myself needing this type of solution a lot, i wonder if theres anything that supports

  • custom delays based on attempts
  • maxRetries
  • promise result validation (if it fails then keep trying)

@icodeforlove
Copy link

@aartajew
Copy link

Working version with delay and try count limit:

function retry(func, count = 5, delay = 1000) {
  return func().then(undefined, (e) => {
    if (count - 1) {
      return new Promise((resolve) => setTimeout(resolve, delay)).then(() =>
        retry(func, count - 1, delay)
      );
    }
    throw e;
  });
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment