Real Cancellable Promise

1.2.3 · active · verified Wed Apr 22

`real-cancellable-promise` is a robust and lightweight library offering a cancellable Promise implementation for JavaScript and TypeScript. Unlike many other approaches that merely prevent callbacks from executing, this library focuses on propagating cancellation signals to the underlying asynchronous operations, such as network requests made with `fetch`, `axios`, or `jQuery.ajax`, thereby releasing resources and truly aborting tasks. It boasts zero dependencies and a minimal footprint, under 1 kB minified and gzipped. The current stable version is 1.2.3, with ongoing active maintenance reflected in its consistent bug fix releases. Key differentiators include its explicit support for 'real' cancellation and its compatibility with popular ecosystems like React (solving issues like `setState` after unmount and handling variable query parameters) and `react-query`'s cancellation features out-of-the-box. It supports modern browsers (excluding Internet Explorer) and Node.js 14+ (with `AbortController` functionality requiring Node 15+).

Common errors

Warnings

Install

Imports

Quickstart

This example demonstrates how to create a `CancellablePromise` using `AbortController` for an HTTP `fetch` request, initiate the request, and then cancel it, explicitly handling the `Cancellation` error. It also shows a successful fetch for comparison.

import { CancellablePromise, Cancellation } from 'real-cancellable-promise';

interface Post { id: number; title: string; body: string; }

// Simulate an API call with AbortController for real cancellation
function fetchPostWithCancellation(postId: number): CancellablePromise<Post> {
  const abortController = new AbortController();
  const signal = abortController.signal;

  const promise = fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`, { signal })
    .then(response => {
      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`);
      }
      return response.json();
    });

  return new CancellablePromise(
    promise,
    () => {
      abortController.abort(new Cancellation(`Request for post ${postId} was cancelled.`));
    }
  );
}

async function runExample() {
  console.log('Fetching post 1...');
  const cancellableFetch = fetchPostWithCancellation(1);

  try {
    // Start the fetch but cancel it after a short delay
    const timeoutId = setTimeout(() => {
      console.log('Attempting to cancel fetch for post 1...');
      cancellableFetch.cancel();
    }, 50);

    const post = await cancellableFetch;
    clearTimeout(timeoutId);
    console.log('Fetched post:', post.title);
  } catch (error) {
    if (error instanceof Cancellation) {
      console.log('Fetch for post 1 was successfully cancelled:', error.message);
    } else {
      console.error('Fetch for post 1 failed:', error);
    }
  }

  // Demonstrate a successful fetch
  console.log('\nFetching post 2...');
  const successfulFetch = fetchPostWithCancellation(2);
  try {
    const post = await successfulFetch;
    console.log('Successfully fetched post 2:', post.title);
  } catch (error) {
    console.error('Fetch for post 2 failed:', error);
  }
}

runExample();

view raw JSON →