Skip to content

Commit 0e688ed

Browse files
authored
Merge pull request #50 from lifeomic/avoid-unhandled-rejections
Avoid unhandled rejections on slow failures
2 parents 3da9c04 + 39f2d29 commit 0e688ed

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,10 @@ export async function retry<T> (
184184
resolve(result);
185185
}).catch((err: any) => {
186186
clearTimeout(timer);
187-
resolve(onError(err));
187+
// Calling resolve with a Promise that rejects here will result
188+
// in an unhandled rejection. Calling `reject` with errors
189+
// does not result in an unhandled rejection
190+
onError(err).then(resolve).catch(reject);
188191
});
189192
});
190193
} else {

test/index.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,3 +583,50 @@ test('should allow handleTimeout to throw an error that can be caught', async (t
583583

584584
t.is(expectedError, actualError);
585585
});
586+
587+
test('errors after the timeout, when using handleTimeout, should not result in unhandled rejections', async (t) => {
588+
const expectedError = new Error('Operation timed out');
589+
590+
// Mock a slow to function that ends up failing
591+
let resolve: (value: any) => void;
592+
const fnPromise = new Promise((_resolve) => {
593+
resolve = _resolve;
594+
});
595+
596+
const fn = async () => {
597+
await fnPromise;
598+
throw new Error('This causes unhandled rejection');
599+
};
600+
601+
const actualError = await t.throws(
602+
retry(fn, {
603+
// Add some configuration to makeit timeout fase
604+
delay: 1,
605+
maxAttempts: 1,
606+
initialDelay: 0,
607+
minDelay: 0,
608+
maxDelay: 0,
609+
factor: 0,
610+
timeout: 10,
611+
handleTimeout: async (context, options) => {
612+
if (context.attemptsRemaining > 0) {
613+
const res = await retry(
614+
fn,
615+
Object.assign({}, options, {
616+
maxAttempts: context.attemptsRemaining
617+
})
618+
);
619+
if (res) return res;
620+
}
621+
throw expectedError;
622+
}
623+
})
624+
);
625+
626+
// Now that the retry failed due to timeout errors, finally allow
627+
// the funciton to throw it's error.
628+
// tslint:disable-next-line:no-unnecessary-type-assertion
629+
resolve!(0);
630+
631+
t.is(actualError, expectedError);
632+
});

0 commit comments

Comments
 (0)