Skip to content

Commit

Permalink
feat: Catch sync functions and throw useful error
Browse files Browse the repository at this point in the history
  • Loading branch information
nzakas committed Jan 17, 2024
1 parent 27970c0 commit 2a39022
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 4 deletions.
19 changes: 16 additions & 3 deletions src/retrier.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export class Retrier {
if (typeof check !== "function") {
throw new Error("Missing function to check errors");
}

this.#timeout = timeout;
this.#check = check;
this.#timeout = timeout;
Expand All @@ -179,9 +179,23 @@ export class Retrier {
* processed.
*/
retry(fn) {

let result;

try {
result = fn();
} catch (error) {
return Promise.reject(new Error("Cannot catch synchronous errors."));
}

// if the result is not a promise then reject an error
if (!(result instanceof Promise)) {
return Promise.reject(new Error("Result is not a promise."));
}

// call the original function and catch any ENFILE or EMFILE errors
// @ts-ignore because we know it's any
return fn().catch(error => {
return result.catch(error => {
if (!this.#check(error)) {
throw error;
}
Expand Down Expand Up @@ -225,7 +239,6 @@ export class Retrier {
// otherwise, try again
task.lastAttempt = Date.now();
task.fn()

// @ts-ignore because we know it's any
.then(result => task.resolve(result))

Expand Down
61 changes: 60 additions & 1 deletion tests/retrier.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//-----------------------------------------------------------------------------

import { Retrier } from "../src/retrier.js";
import { assert } from "chai";
import assert from "node:assert";

//-----------------------------------------------------------------------------
// Tests
Expand All @@ -27,4 +27,63 @@ describe("Retrier", () => {

});

describe("retry()", () => {

it("should retry a function that rejects an error", async () => {

let count = 0;
const retrier = new Retrier(error => error.message === "foo");
const result = await retrier.retry(async () => {
count++;

if (count === 1) {
throw new Error("foo");
}

return count;
});

assert.equal(result, 2);
});

it("should retry a function that rejects an error multiple times", async () => {

let count = 0;
const retrier = new Retrier(error => error.message === "foo");
const result = await retrier.retry(async () => {
count++;

if (count < 5) {
throw new Error("foo");
}

return count;
});

assert.equal(result, 5);
});

it("should reject an error when the function is synchronous", async () => {

const retrier = new Retrier(error => error.message === "foo");

assert.rejects(async () => {
await retrier.retry(() => {
throw new Error("foo");
});
}, /Cannot catch synchronous errors/);
});

it("should reject an error when the function doesn't return a promise", async () => {

const retrier = new Retrier(error => error.message === "foo");

assert.rejects(async () => {
// @ts-expect-error
await retrier.retry(() => {});
}, /Result is not a promise/);
});

});

});

0 comments on commit 2a39022

Please sign in to comment.