Skip to content
VersionSize

once

The once utility restricts a function so that it can only be executed a single time. Subsequent calls to the restricted function will return the result of the first invocation. It also includes a reset method to allow the function to be run again if needed.

Implementation

View Source Code
ts
import type { Fn } from '../types';

/**
 * Create a function that runs once and returns the first result.
 *
 * @example
 * ```ts
 * const onceRandom = once(() => Math.random())
 * onceRandom() // 0.3
 * onceRandom() // 0.3
 *
 * onceRandom.reset()
 *
 * onceRandom() // 0.2
 * onceRandom() // 0.2
 * ```
 *
 * @param fn - The function to wrap.
 *
 * @returns A function that can only be called once.
 */
export const once = <T extends Fn>(fn: T): T & { reset: () => void } => {
  let result: ReturnType<T> | undefined;
  let called = false;

  const wrappedFn = ((...args: Parameters<T>): ReturnType<T> => {
    if (!called) {
      result = fn(...args);
      called = true;
    }
    return result as ReturnType<T>;
  }) as T & { reset: () => void };

  wrappedFn.reset = () => {
    result = undefined;
    called = false;
  };

  return wrappedFn;
};

Features

  • Isomorphic: Works in both Browser and Node.js.
  • Stateful: Remembers the result of the first call.
  • Resettable: Manual control to clear the cached result and allow a new execution.
  • Type-safe: Preserves the argument and return types of the original function.

API

ts
function once<T extends (...args: any[]) => any>(fn: T): T;

Parameters

  • fn: The function to restrict.

Returns

  • A new function that only executes fn once.
  • The returned function has a .reset() property to clear its state.

Examples

One-time Initialization

ts
import { once } from '@vielzeug/toolkit';

const initializeApp = once(() => {
  console.log('Connecting to database...');
  return { status: 'ready' };
});

initializeApp(); // Logs message, returns { status: 'ready' }
initializeApp(); // Returns same object, no log

Resettable Logic

ts
import { once } from '@vielzeug/toolkit';

const getData = once(() => fetch('/api/data'));

await getData(); // Performs fetch
await getData(); // Returns cached promise

// Force a new fetch later
getData.reset();
await getData(); // Performs new fetch

Implementation Notes

  • Performance-optimized using a simple closure.
  • The .reset() method is ideal for handling logout/re-login scenarios or refreshing stale caches.
  • Throws TypeError if fn is not a function.

See Also

  • memo: Cache results based on multiple different arguments.
  • throttle: Rate-limit execution based on time.
  • debounce: Delay execution until a quiet period.