Skip to content

once

Edit

Invokes a function once and reuses its return value or thrown error.

once.ts
type OnceState<Return> =
| { status: "idle" }
| { status: "running" }
| { status: "returned"; value: Return }
| { status: "threw"; error: unknown };
export type OnceFunction<
Arguments extends unknown[],
Return,
This = unknown,
> = (this: This, ...args: Arguments) => Return;
type AnyFunction = (this: never, ...args: never[]) => unknown;
/** Invokes a function once, then reuses its return value or thrown error. */
export const once = <Function extends AnyFunction>(
fn: Function
): OnceFunction<
Parameters<Function>,
ReturnType<Function>,
ThisParameterType<Function>
> => {
let state: OnceState<ReturnType<Function>> = { status: "idle" };
const wrapped = function (
this: ThisParameterType<Function>,
...args: Parameters<Function>
): ReturnType<Function> {
if (state.status === "returned") return state.value;
if (state.status === "threw") throw state.error;
if (state.status === "running") {
throw new Error("once callback cannot invoke itself recursively");
}
state = { status: "running" };
try {
const value = Reflect.apply(fn, this, args) as ReturnType<Function>;
state = { status: "returned", value };
return value;
} catch (error) {
state = { status: "threw", error };
throw error;
}
};
return wrapped;
};
Terminal
wget -O src/lib/once.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/functions/once.ts
once.example.ts
import { once } from "./once";
// Function that we want to run only once
const initialize = () => console.log("Initialization completed!");
const initializeOnce = once(initialize);
// Call the function multiple times
initializeOnce(); // Output: Initialization completed!
initializeOnce(); // No output
initializeOnce(); // No output
  • fn ((...args: A) => R) — Function invoked with the arguments and receiver from the first call.

(...args: A) => R — A wrapper that returns the first result on later calls. If the first invocation throws, later calls rethrow the same value.