Skip to content

Commit

Permalink
catch-timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
uriva committed Mar 4, 2024
1 parent 17003b6 commit 16311ff
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 5 deletions.
26 changes: 26 additions & 0 deletions src/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,29 @@ export const tryCatch = <F extends Func, T>(
}) as AugmentReturnType<F, T>;

export const catchWithNull = <F extends Func>(f: F) => tryCatch(f, () => null);

export const catchSpecificError = (error: Error) =>
// deno-lint-ignore no-explicit-any
<F extends AsyncFunction, G extends (...args: Parameters<F>) => any>(
fallback: G,
f: F,
): (...args: Parameters<F>) => ReturnType<F> | ReturnType<G> =>
// @ts-expect-error cannot infer
async (...xs: Parameters<F>) => {
try {
return await f(...xs);
} catch (e) {
console.log(e === error)
if (e === error) return fallback(...xs);
throw e;
}
};

export const throwerCatcher = () => {
const specificError = new Error();
const catcher = catchSpecificError(specificError);
const thrower = () => {
throw specificError;
};
return [thrower, catcher] as [typeof thrower, typeof catcher];
};
28 changes: 28 additions & 0 deletions src/io.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { mapCat } from "./map.ts";
import { repeat } from "./matrix.ts";
import { wrapPromise } from "./promise.ts";
import { sleep } from "./time.ts";
import { timerCatcher } from "./io.ts";

const sumOfThings = (numbers: number[]) =>
wrapPromise(numbers.reduce((acc, current) => acc + current, 0));
Expand Down Expand Up @@ -131,6 +132,33 @@ Deno.test("timeout triggers if not ended in time", async () => {
await sleep(100); // Wait for the interval to finish.
});

Deno.test("timeout can be caught", async () => {
const [timer, catcher] = timerCatcher();
const f = timer(10, async () => {
await sleep(50);
return success;
});
assertEquals(
await catcher(f, () => Promise.resolve(failed))(),
failed,
);
await sleep(100); // Wait for the interval to finish.
});

Deno.test("timeout catching is specifc", async () => {
const failed2 = {};
const [timer, catcher] = timerCatcher();
const f = timer(10, async () => {
await sleep(0);
throw new Error("unrelated error");
});
assertEquals(
await catcher(() => Promise.resolve(failed), f)().catch(() => failed2),
failed2,
);
await sleep(100); // Wait for the interval to finish.
});

Deno.test("retry", async () => {
let c = 0;
const succeedOn3rdAttempt = async (x: number) => {
Expand Down
20 changes: 15 additions & 5 deletions src/io.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { juxt, pairRight, stack } from "./juxt.ts";
import { prop, spread } from "./operator.ts";

import { AsyncFunction, ElementOf } from "./typing.ts";
import { applySpec } from "./mapping.ts";
import { map } from "./map.ts";
import { pipe } from "./composition.ts";
import { catchSpecificError } from "./debug.ts";
import { map } from "./map.ts";
import { applySpec } from "./mapping.ts";
import { sleep } from "./time.ts";
import { AsyncFunction, ElementOf } from "./typing.ts";

type Executor<TaskInput, Output> = (_: TaskInput[]) => Promise<Output>;

Expand Down Expand Up @@ -97,7 +98,15 @@ export const batch = <
);
};

export const timeout = <F extends AsyncFunction>(
export const timerCatcher = () => {
const error = new Error("Timed out");
const catcher = catchSpecificError(error);
const thrower = timeoutHelper(error);
return [thrower, catcher] as [typeof thrower, typeof catcher];
};

const timeoutHelper = (error: Error) =>
<F extends AsyncFunction>(
ms: number,
f: F,
): F =>
Expand All @@ -106,7 +115,6 @@ export const timeout = <F extends AsyncFunction>(
new Promise((resolve, reject) => {
let wasResolved = false;
let rejected = false;
const error = new Error(`Timed out after ${ms} ms`);
const timer = setTimeout(() => {
if (wasResolved) return;
rejected = true;
Expand All @@ -120,6 +128,8 @@ export const timeout = <F extends AsyncFunction>(
}).catch(reject);
});

export const timeout = timeoutHelper(new Error(`Timed out`));

export const conditionalRetry =
// deno-lint-ignore no-explicit-any
(predicate: (e: Error) => any) =>
Expand Down

0 comments on commit 16311ff

Please sign in to comment.