Retry with backoff in JavaScript
Try again, waiting longer each time, and give up eventually.
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function retry(work, { attempts = 4, base = 5 } = {}) {
let lastError;
for (let attempt = 1; attempt <= attempts; attempt += 1) {
try {
return await work(attempt);
} catch (error) {
lastError = error;
if (attempt === attempts) break;
await sleep(base * 2 ** (attempt - 1));
}
}
throw new Error(`failed after ${attempts} attempts`, { cause: lastError });
}
async function main() {
const value = await retry(async (attempt) => {
if (attempt < 3) throw new Error(`attempt ${attempt} failed`);
return "succeeded";
});
console.log(value);
const failed = await retry(async () => {
throw new Error("always");
}, { attempts: 2 }).catch((e) => e);
console.log(failed.message, "|", failed.cause.message);
}
main();
How it works
- The delay doubles, which is what backoff means.
- Only retry what is worth retrying.
- Attach the last error as the cause when giving up.
Keywords and builtins used here
Promiseasyncawaitbreakcatchconstforfunctionifletreturnthrowtry
The run, in numbers
- Lines
- 30
- Characters to type
- 787
- Tokens
- 232
- Three-star pace
- 110 tpm
At the three-star pace of 110 tokens a minute, this run takes about 127 seconds.
Step 3 of 4 in Canceling & limiting, step 17 of 19 in Promises & async.