Skip to content

Latest commit

 

History

History

Chain an AbortController to multiple AbortSignals

npm GitHub Workflow Status license Codecov

Chain multiple signals into one controller: if any of the signals aborts, the controller will be aborted. This is particularly useful since most APIs only accept a single AbortSignal input (fetch(), axios, the AWS SDK, etc).

Works with browsers, NodeJS (16+), and spec-compliant polyfills.

Usage

At a minimum, you must pass one or more AbortSignals. However, the controller is optional: if a controller is not passed in, one will be created and returned.

import { chainAbortController } from "abortcontroller-chain";

// use your own controller
chainAbortController(controller, signal1, signal2, signal3);

// auto-create the controller
const controller = chainAbortController(signal1, signal2, signal3);

Why

Say you're passing an AbortSignal into a long-running piece of code so you can end early if needed:

async function pollSomething(signal: AbortSignal) {
  while (!signal.aborted) {
    // keep polling until the signal says stop
  }
}

But wait! You want to make an HTTP request with a deadline of 10 seconds:

const TEN_SECONDS = 10 * 1000; // ms
async function pollSomething(signal: AbortSignal) {
  while (!signal.aborted) {
    // abort after 500ms
    const abortController = new AbortController();
    setTimeout(() => abortController.abort(), TEN_SECONDS);

    // do the fetch...
    const result = await fetch("/something-something", {
      // wait, this doesn't take into account the signal
      // passed to pollSomething()
      signal: abortController.signal,
    });

    /* ... */
  }
}

Wouldn't it be great if the fetch-specific abortController could be aborted by the outer signal? That's easy with the abortcontroller-chain package:

import { chainAbortController } from "abortcontroller-chain";

const TEN_SECONDS = 10 * 1000; // ms
async function pollSomething(signal: AbortSignal) {
  while (!signal.aborted) {
    const abortController = new AbortController();
    chainAbortController(abortController, signal);
    setTimeout(() => abortController.abort(), TEN_SECONDS);

    // do the fetch...
    const result = await fetch("/something-something", {
      // hooray! both signals are now respected
      signal: abortController.signal,
    });

    /* ... */
  }
}

You can further simplify the creation:

const abortController = chainAbortController(signal);
setTimeout(() => abortController.abort(), TEN_SECONDS);