There is no way to cancel all the network requests globallly at once. you have to attach an abort controller to each fetch calls.

import { useEffect } from 'react';

export const useAbortController = (fetcher,args,dependencies) => {
  useEffect(() => {
    const abortController = new AbortController();
    const signal = abortController.signal;
    // fetch here. write a reusable form based on your api function
    fetcher(...args,{signal})

    // you could also setTimeout and maybe after 2 seconds call abortController.abort()
      return () => abortController.abort();
  }, [...dependencies]);
};
Answer from Yilmaz on Stack Overflow
🌐
DEV Community
dev.to › bil › using-abortcontroller-with-react-hooks-and-typescript-to-cancel-window-fetch-requests-1md4
Using AbortController (with React Hooks and TypeScript) to cancel window.fetch requests - DEV Community
March 27, 2019 - // src/hooks/useProfileInformation.jsx import {useState, useEffect} from 'react' export function useProfileInformation({accessToken}) { const [profileInfo, setProfileInfo] = useState(null) useEffect(() => { const abortController = new AbortController() window .fetch('https://api.example.com/v1/me', { headers: {Authorization: `Bearer ${accessToken}`}, method: 'GET', mode: 'cors', signal: abortController.signal, }) .then(res => res.json()) .then(res => setProfileInfo(res.profileInfo)) return function cancel() { abortController.abort() } }, [accessToken]) return profileInfo } // src/app.jsx impor
🌐
j-labs
j-labs.pl › home › tech blog › how to use the useeffect hook with the abortcontroller
AbortController in React. How to use the useEffect hook with the AbortControl? | j‑labs
December 9, 2025 - The AbortController interface provides a standardized mechanism to cancel ongoing asynchronous operations in JavaScript. Integrating AbortController with React’s useEffect hook prevents memory leaks by canceling requests when components unmount.
🌐
Medium
medium.com › @icjoseph › using-react-to-understand-abort-controllers-eb10654485df
Using React to understand Abort Controllers | by Joseph Chamochumbi | Medium
November 18, 2022 - And there’s the AbortController, which fits both cases. You’d use this in a React useEffect hook like this:
🌐
Bram.us
bram.us › 2020 › 04 › 08 › useabortcontroller-a-react-hook-to-work-with-the-abortcontroller
useAbortController – A React Hook to work with the AbortController
April 8, 2020 - Kent C. Dodds recently floated this snippet around, a React Hook to easily work with the the AbortController: function useAbortController() { const abortControllerRef = React.useRef() const getAbortController = React.useCallback(() => { if (!abortControllerRef.current) { abortControllerRef.current = new AbortController() } return abortControllerRef.current }, []) React.useEffect(() => { return () => getAbortController().abort() }, [getAbortController]) const getSignal = … Continue reading "useAbortController – A React Hook to work with the AbortController"
🌐
GitHub
github.com › closeio › use-abortable-effect
GitHub - closeio/use-abortable-effect: Super simple React hook for running abortable effects based on the AbortController API.
*/) .catch((rejection) => { if (rejection.name !== 'AbortError') { // Re-throw or handle non-abort rejection in another way. return Promise.reject(rejection); } }), ); const handleManualAbort = () => abortControllerRef.current.abort(); // … } import React from 'react'; import useAbortableEffect from '@closeio/use-abortable-effect'; export default function MyAbortableComputationComponent() { const abortControllerRef = useAbortableEffect(abortSignal => { new Promise((resolve, reject) => { // Should be a DOMException per spec.
Author   closeio
Find elsewhere
🌐
Close
making.close.com › posts › introducting-use-abortable-effect-react-hook
Introducing useAbortableEffect: a simple React hook for running abortable effects | The Making of Close
June 18, 2020 - API is compatible with useEffect, where the effect function you pass-in accepts an AbortSignal instance as a param and you can return a cleanup function that accepts an AbortController instance. Supports abortable fetch requests. Supports running custom operations/computations that can be easily aborted. Auto-aborts effects on re-run (or component unmount), unless you provide a custom cleanup function. // Regular React effect hook.
Top answer
1 of 3
22

Based off your code, there are a few corrections to make:

Don't return new Promise() inside an async function

You use new Promise if you're taking something event-based but naturally asynchronous, and wrap it into a Promise. Examples:

  • setTimeout
  • Web Worker messages
  • FileReader events

But in an async function, your return value will already be converted to a promise. Rejections will automatically be converted to exceptions you can catch with try/catch. Example:

async function MyAsyncFunction(): Promise<number> {
  try {
    const value1 = await functionThatReturnsPromise(); // unwraps promise 
    const value2 = await anotherPromiseReturner();     // unwraps promise
    if (problem)
      throw new Error('I throw, caller gets a promise that is eventually rejected')
    return value1 + value2; // I return a value, caller gets a promise that is eventually resolved
  } catch(e) {
    // rejected promise and other errors caught here
    console.error(e);
    throw e; // rethrow to caller
  }
}

The caller will get a promise right away, but it won't be resolved until the code hits the return statement or a throw.

What if you have work that needs to be wrapped with a Promise constructor, and you want to do it from an async function? Put the Promise constructor in a separate, non-async function. Then await the non-async function from the async function.

function wrapSomeApi() {
  return new Promise(...);
}

async function myAsyncFunction() {
  await wrapSomeApi();
}

When using new Promise(...), the promise must be returned before the work is done

Your code should roughly follow this pattern:

function MyAsyncWrapper() {
  return new Promise((resolve, reject) => {
    const workDoer = new WorkDoer();
    workDoer.on('done', result => resolve(result));
    workDoer.on('error', error => reject(error));
    // exits right away while work completes in background
  })
}

You almost never want to use Promise.resolve(value) or Promise.reject(error). Those are only for cases where you have an interface that needs a promise but you already have the value.

AbortController is for fetch only

The folks that run TC39 have been trying to figure out cancellation for a while, but right now there's no official cancellation API.

AbortController is accepted by fetch for cancelling HTTP requests, and that is useful. But it's not meant for cancelling regular old work.

Luckily, you can do it yourself. Everything with async/await is a co-routine, there's no pre-emptive multitasking where you can abort a thread or force a rejection. Instead, you can create a simple token object and pass it to your long running async function:

const token = { cancelled: false }; 
await doLongRunningTask(params, token); 

To do the cancellation, just change the value of cancelled.

someElement.on('click', () => token.cancelled = true); 

Long running work usually involves some kind of loop. Just check the token in the loop, and exit the loop if it's cancelled

async function doLongRunningTask(params: string, token: { cancelled: boolean }) {
  for (const task of workToDo()) {
    if (token.cancelled)
      throw new Error('task got cancelled');
    await task.doStep();
  }
}

Since you're using react, you need token to be the same reference between renders. So, you can use the useRef hook for this:

function useCancelToken() {
  const token = useRef({ cancelled: false });
  const cancel = () => token.current.cancelled = true;
  return [token.current, cancel];
}

const [token, cancel] = useCancelToken();

// ...

return <>
  <button onClick={ () => doLongRunningTask(token) }>Start work</button>
  <button onClick={ () => cancel() }>Cancel</button>
</>;

hash-wasm is only semi-async

You mentioned you were using hash-wasm. This library looks async, as all its APIs return promises. But in reality, it's only await-ing on the WASM loader. That gets cached after the first run, and after that all the calculations are synchronous.

Even if it is wrapped in an async function or in a function returning a Promise, code must yield the thread to act concurrently, which hash-wasm does not appear to do in its main computation loop.

So how can you let your code breath if you've got CPU intensive code like what hash-wasm uses? You can do your work in increments, and schedule those increments with setTimeout:

for (const step of stepsToDo) {
  if (token.cancelled)
    throw new Error('task got cancelled');

  // schedule the step to run ASAP, but let other events process first
  await new Promise(resolve => setTimeout(resolve, 0));

  const chunk = await loadChunk();
  updateHash(chunk);
}

(Note that I'm using a Promise constructor here, but awaiting immediately instead of returning it)

The technique above will run slower than just doing the task. But by yielding the thread, stuff like React updates can execute without an awkward hang.

If you really need performance, check out Web Workers, which let you do CPU-heavy work off-thread so it doesn't block the main thread. Libraries like workerize can help you convert async functions to run in a worker.


That's everything I have for now, I'm sorry for writing a novel

2 of 3
0

I can suggest my library (use-async-effect2) for managing the cancellation of asynchronous tasks/promises. Here is a simple demo with nested async function cancellation:

    import React, { useState } from "react";
    import { useAsyncCallback } from "use-async-effect2";
    import { CPromise } from "c-promise2";
    
    // just for testing
    const factorialAsync = CPromise.promisify(function* (n) {
      console.log(`factorialAsync::${n}`);
      yield CPromise.delay(500);
      return n != 1 ? n * (yield factorialAsync(n - 1)) : 1;
    });
    
    function TestComponent({ url, timeout }) {
      const [text, setText] = useState("");
    
      const myTask = useAsyncCallback(
        function* (n) {
          for (let i = 0; i <= 5; i++) {
            setText(`Working...${i}`);
            yield CPromise.delay(500);
          }
          setText(`Calculating Factorial of ${n}`);
          const factorial = yield factorialAsync(n);
          setText(`Done! Factorial=${factorial}`);
        },
        { cancelPrevious: true }
      );
    
      return (
        <div>
          <div>{text}</div>
          <button onClick={() => myTask(15)}>
            Run task
          </button>
          <button onClick={myTask.cancel}>
            Cancel task
          </button>
        </div>
      );
    }
🌐
ITNEXT
itnext.io › developing-react-custom-hooks-for-abortable-async-functions-with-abortcontroller-1f2091b5a1c3
Developing React custom hooks for abortable async functions with AbortController | by Daishi Kato | ITNEXT
December 7, 2021 - In my previous article, I introduced how the custom hook useAsyncTask handles async functions with AbortController and demonstrated a typeahead search example. In this article, I explain about the…
🌐
CodeSandbox
codesandbox.io › s › react-hooks-abortcontroller-wbiju
React Hooks AbortController - CodeSandbox
October 26, 2019 - React Hooks AbortController by jamesmosier using react, react-dom, react-scripts
Published   Oct 26, 2019
Author   jamesmosier
🌐
LogRocket
blog.logrocket.com › home › the complete guide to the abortcontroller api
The complete guide to the AbortController API - LogRocket Blog
March 12, 2025 - This tutorial will offer a complete guide on how to use the AbortController and AbortSignal APIs in both your backend and frontend. In our case, we’ll focus on Node.js and React.
🌐
Medium
eminfurkan.medium.com › managing-asynchronous-operations-with-abortcontroller-in-react-e9bec3565ec8
Managing Asynchronous Operations with AbortController in React | by Furkan Tezeren | Medium
July 30, 2023 - We initiate the fetch operation when the component is first rendered using the useEffect hook. The returned function inside useEffect is used to cancel the operation using the abortController when the component unmounts. This way, when the component unmounts, we can cancel the fetch operation, preventing any unwanted operations from running and ensuring better control over our asynchronous tasks. Lastly, Let’s create a custom React hook called useAbortableFetch to handle asynchronous HTTP requests with the AbortController.
🌐
GitHub
gist.github.com › kentcdodds › b36572b6e9227207e6c71fd80e63f3b4
abort-controller.js · GitHub
function useAbortController() { const abortControllerRef = React.useRef<AbortController>() React.useEffect(() => { return () => abortControllerRef.current?.abort() }, []) const getSignal = React.useCallback(() => { if (!abortControllerRef.current) { abortControllerRef.current = new AbortController() } return abortControllerRef.current.signal }, []) return getSignal } ... Whoever found above gist first before Kent's own tweet, use the above hook like below.
🌐
Westbrookdaniel
westbrookdaniel.com › blog › react-abort-controllers
Using AbortControllers to Cancel Fetch in React - Daniel Westbrook
Here we use the web api AbortController as the signal for fetch. By returning a function from useEffect we can trigger the abort controller on dismount (see the React docs).
🌐
Wanago
wanago.io › home › using abortcontroller to deal with race conditions in react
Using AbortController to deal with race conditions in React
April 11, 2022 - The most straightforward fix for the above issue is introducing a didCancel variable, as suggested by Dan Abramov. When doing that, we need to use the fact that we can clean up after our useEffect hook.
🌐
DEV Community
dev.to › mr_mornin_star › how-to-synchronize-your-api-calls-with-your-component-lifecycle-in-react-with-a-custom-hook-82d
Custom React Hook to cancel network calls, and then synchronize API calls with component life cycle - DEV Community
September 14, 2022 - As of now browser's fetch API and Axios officially supports AbortControllers · Now we can just be done with this here, but just to make it look a little bit cooler let's make a custom hook out of this and look at a live example · import { useEffect } from 'react'; const useAbortedEffect = ( effect: (signal: AbortSignal) => Function | void, dependencies: Array<any> ) => { useEffect(() => { const abortController = new AbortController(); const signal = abortController.signal; const cleanupEffect = effect(signal); return () => { if (cleanupEffect) { cleanupEffect(); } abortController.abort(); }; }, [...dependencies]); }; export default useAbortedEffect;
🌐
GitHub
github.com › facebook › react › issues › 25284
Bug: React.StrictMode causes AbortController to cancel · Issue #25284 · facebook/react
September 16, 2022 - const useAbortController = (abortControllerProp, shouldAutoRestart = false) => { const abortController = useRef(abortControllerProp || initAbortController()); useEffect(() => { if (shouldAutoRestart && abortController.current.signal.aborted) { abortController.current = initAbortController(); } }, [abortController.current.signal.aborted, shouldAutoRestart]); useEffect(() => () => abortController.current.abort(), []); return abortController.current; }; The "echoed" rendering of the component causes the the controller to go from aborted false -> true. I'm not sure if this is inherent to what react tests for in this mode, or something that can be expected to work.
Author   EdmundsEcho
🌐
Kettanaito
kettanaito.com › blog › dont-sleep-on-abort-controller
Don't Sleep on AbortController - kettanaito.com
September 17, 2024 - An AbortController instance is also much nicer to pass around if a different part of your application is responsible for removing the listener. A great "aha" moment for me was when I realized you can use a single signal to remove multiple event listeners! ... In the example above, I'm adding a useEffect() hook in React that introduces a bunch of event listeners with different purpose and logic.