saborter

Documentation for library functions

📖 Overview

This is an overview of the helper functions available in the package. These utilities are designed to simplify working with AbortSignal, debouncing, timeouts, and time conversions.

The functions below are based on the AbortError class. It is used to provide consistent and informative error objects when an operation is aborted.

🔧 API

Import

import { isAbortError, debounce } from 'saborter/lib';

isAbortError

A type guard function that determines whether a given error is an AbortError. It uses multiple heuristics to identify abort errors, including checking the error’s type, its name property, its message content, and recursively examining the error’s cause chain.

Parameters:

Returns:

error is Error - type guard.

Returns true if the value is identified as an AbortError, otherwise false. When true, TypeScript narrows the type to Error.

Description:

The function performs the following checks in order:

  1. Instance check: If error is an instance of AbortError, it returns true.
  2. Name property check: If error is an object and has a name property equal to 'AbortError', it returns true.
  3. Message substring check: If error has a message property that contains the substring 'abort', it returns true. This matches common error messages like “The operation was aborted”.
  4. Cause chain check: It recursively checks the error’s cause property (if any). If any error in the cause chain satisfies one of the above conditions, it returns true.

If none of these checks pass, it returns false.

This lenient approach ensures that various representations of abort errors (e.g., standard AbortError instances, plain objects, or errors with a nested cause) are correctly identified, making it useful in environments where the exact error class may not be available or where errors are wrapped.

[!IMPORTANT]

Examples:

Direct instance:

const abortError = new AbortError('Aborted');
isAbortError(abortError); // true

Object with correct name:

const fakeAbort = { name: 'AbortError', message: 'Cancelled' };
isAbortError(fakeAbort); // true

Error with message containing ‘abort’:

const error = new Error('The operation was aborted');
isAbortError(error); // true

Error with cause chain:

const inner = new AbortError('Inner abort');
const outer = new Error('Wrapper', { cause: inner });
isAbortError(outer); // true

Non-abort errors:

const regularError = new Error('Something went wrong');
isAbortError(regularError); // false

const nullValue = null;
isAbortError(nullValue); // false

catchAbortError

This function catches errors that are AbortError’s and ignores them, while re‑throwing any other error. It is useful in scenarios where you want to handle abort errors silently (e.g., because they are expected and you don’t need to act on them) but still propagate genuine errors.

Parameters:

Returns:

void | never

Example:

try {
  await fetchWithTimeout(url, { signal });
} catch (error) {
  catchAbortError(error); // Non‑abort errors are re‑thrown; abort errors are ignored.
  // If execution reaches this point, it means the error was an AbortError and we can ignore it.
}

debounce

Creates a debounced function that delays invoking the provided handler until after a specified timeout has elapsed since the last call. This is a leading‑edge debounce – the first call in a burst schedules the execution, and subsequent calls reset the timer. The debounced function accepts an AbortSignal and returns a promise that resolves with the handler’s result or rejects if the handler throws or the signal is aborted.

Parameters:

Returns:

(signal: AbortSignal) => R | Promise<R>

A function that accepts an AbortSignal and returns a Promise<R>, where R is the return type of the handler.

Error handling:

Examples:

const debouncedFetch = debounce((signal) => fetch('/api/search', { signal }), 300);

const controller = new AbortController();
debouncedFetch(controller.signal)
  .then((response) => response.json())
  .catch((err) => {
    if (err instanceof AbortError) {
      console.log('Debounced call aborted by:', err.initiator); // 'debounce'
    }
  });

You can also use debounce in conjunction with Aborter to delay the request for a while:

const data = await aborter.try(debounce((signal) => fetch('/api/data', { signal }), 300));

isAbortSignal

A type guard that checks whether a given value is an instance of AbortSignal.

Parameters:

Returns:

boolean – true if the value is an AbortSignal, false otherwise.

Example:

const controller = new AbortController();
console.log(isAbortSignal(controller.signal)); // true
console.log(isAbortSignal({})); // false

rethrowAbortError

This function is the complement of catchAbortError. It re‑throws the error only if it is an AbortError; otherwise, it does nothing. This is useful in error‑handling patterns where you want to let abort errors propagate (so they can be caught elsewhere) while handling other errors locally.

Parameters:

Returns:

void | never

Example:

try {
  await someOperation(signal);
} catch (error) {
  rethrowAbortError(error); // Only re‑throws if it's an abort error.
  // Handle other errors here.
}

setTimeoutAsync

Schedules the execution of a handler after a specified delay. The operation can be cancelled using an AbortSignal. This function returns a promise that resolves with the handler’s result or rejects if the timeout is aborted or if the handler throws an error.

Parameters:

Returns:

Promise<T> – resolves with the handler’s result, or rejects with an AbortError if the operation was aborted, or with any error thrown by the handler.

Behavior:

Examples:

const controller = new AbortController();
setTimeoutAsync(
  (signal) => {
    return fetch('/api/data', { signal }).then((res) => res.json());
  },
  5000,
  {
    signal: controller.signal
  }
)
  .then((data) => console.log(data))
  .catch((error) => console.log(error.name)); // 'AbortError' if aborted

Option for working with try-catch syntax:

const controller = new AbortController();
try {
  const data = await setTimeoutAsync(
    async (signal) => {
      const response = await fetch('/api/data', { signal });
      return await response.json();
    },
    5000,
    { signal: controller.signal }
  );
} catch (error) {
  console.log(error.name); // 'AbortError' if aborted
}

The setTimeoutAsync function can be used as a delay function if you need it:

const delay = (ms: number) => setTimeoutAsync(() => null, ms);

console.log('Hello');
await delay(2000);
console.log('World');

The setTimeoutAsync function can be used like a regular native setTimeout function, but instead of returning an ID for deletion from the EventLoop, it returns the values ​​you returned from the callback:

// Getting a token from local storage in 5 seconds
const token = await setTimeoutAsync(() => localStorage.getItem('token'), 5000); // string | null

To interrupt the function, we use the AbortSignal:

const controller = new AbortController();
// Or you can use an ReusableAborter
try {
  const token = await setTimeoutAsync(
    () => {
      return localStorage.getItem('token');
    },
    5000,
    { signal: controller.signal }
  );
} catch (error) {
  if (isAbortError(error)) {
    console.log(error.name); // 'AbortError'
  }
}

throwIfAborted

A utility that checks whether an AbortSignal has been aborted. If the signal is aborted, it throws an AbortError. This is useful for manual abortion checks inside long‑running operations that do not automatically handle the signal.

Parameters:

Returns:

void | never

Behavior:

Example:

const processItems = (items: unknown[], signal: AbortSignal) => {
  try {
    for (const item of items) {
      throwIfAborted(signal); // Check before each iteration
      // Process item...
    }
  } catch (error) {
    // Handle error
  }
};

abortSignalAny

Combines multiple abort signals into a single signal that aborts when any of the input signals aborts. This is useful when you need to cancel an operation if any of several independent signals (e.g., from different sources) become aborted.

Signature:

export const abortSignalAny = <T extends AbortSignalLike | AbortSignalLike[]>(...args: T[]): AbortSignal

Where AbortSignalLike = AbortSignal | null | undefined.

Parameters:

This function accepts either an unlimited number of signals or an array of signals.

Returns:

A new AbortController.signal that will be aborted when any of the input signals aborts. If any input signal is already aborted when the function is called, the returned signal is immediately aborted.

Description:

The function works as follows:

  1. Flattens the provided arguments into a single array of signals (ignoring null or undefined).
  2. Creates a new AbortController.
  3. For each signal in the flattened list:

The function ensures that the controller is aborted only once, even if multiple signals abort simultaneously or in quick succession.

Error Handling:

The function creates a consistent AbortError object when aborting the controller. It uses the helper createAbortError, which:

In all cases, the resulting error has an initiator property set to 'abortSignalAny' to help trace the source of the abort.

Examples:

Basic usage with two signals:

import { abortSignalAny } from '.saborter/lib';

const ac1 = new AbortController();
const ac2 = new AbortController();
const combined = abortSignalAny(ac1.signal, ac2.signal);

combined.addEventListener('abort', () => console.log('Combined signal aborted!'));
ac1.abort(); // triggers combined abort

Using arrays:

const signals = [ac1.signal, ac2.signal];
const combined = abortSignalAny(signals);

Handling already aborted signals:

const ac = new AbortController();
ac.abort(); // signal is already aborted

const combined = abortSignalAny(ac.signal); // returns an already aborted signal
combined.aborted; // true

Ignoring null or undefined:

const combined = abortSignalAny(null, undefined, ac.signal); // only ac.signal is considered

timeInMilliseconds

Converts a configuration object containing time components (hours, minutes, seconds, milliseconds) into a total number of milliseconds. All components are optional and default to 0 if not provided.

Parameters:

Returns:

number - the total time in milliseconds.

Throws:

Example:

timeInMilliseconds({ seconds: 1 }); // Returns 1000
timeInMilliseconds({ minutes: 1, seconds: 30 }); // Returns 90000
timeInMilliseconds({ hours: 1, minutes: 30, seconds: 45, milliseconds: 500 }); // Returns 5,445,500

dispose

A function that allows you to clear an object’s data, if the object supports this feature.

Parameters:

Returns:

void - The function does not return anything.

Throws: