From 70f33c05fc665a58c6b2d6fbf8300101eecd7558 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Fri, 3 May 2024 10:48:01 -0400 Subject: [PATCH] fix: Allow retrying with non-Promise thenables --- src/retrier.js | 8 +++++--- tests/retrier.test.js | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/retrier.js b/src/retrier.js index 7658235..5a5ff18 100644 --- a/src/retrier.js +++ b/src/retrier.js @@ -191,13 +191,13 @@ export class Retrier { } // if the result is not a promise then reject an error - if (!(result instanceof Promise)) { + if (!result || typeof result.then !== "function") { 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 result.catch(error => { + return Promise.resolve(result).catch(error => { if (!this.#check(error)) { throw error; } @@ -240,7 +240,9 @@ export class Retrier { // otherwise, try again task.lastAttempt = Date.now(); - task.fn() + + // Promise.resolve needed in case it's a thenable but not a Promise + Promise.resolve(task.fn()) // @ts-ignore because we know it's any .then(result => task.resolve(result)) diff --git a/tests/retrier.test.js b/tests/retrier.test.js index 58ff2a5..aefb59e 100644 --- a/tests/retrier.test.js +++ b/tests/retrier.test.js @@ -46,6 +46,33 @@ describe("Retrier", () => { assert.equal(result, 2); }); + it("should retry a function that rejects an error using a non-Promise thenable", async () => { + + let count = 0; + const retrier = new Retrier(error => error.message === "foo"); + const result = await retrier.retry(() => { + count++; + + if (count === 1) { + return { + then() { + throw new Error("foo"); + } + }; + } + + return { + then(fn) { + fn(count); + } + }; + }); + + assert.equal(result, 2); + }); + + + it("should retry a function that rejects an error multiple times", async () => { let count = 0;