Knowing Abort Controllers: Elegant Cleanup for Async Operations

10 min read
javascript react async

In modern frontend development, asynchronous operations are everywhere — HTTP requests, timers, event listeners, and more.

In frameworks like React, where components mount and unmount dynamically, it’s crucial to manage these async tasks properly to avoid memory leaks and state update errors.

That’s where AbortController comes in. It’s a powerful native JavaScript class that lets you cancel ongoing async operations or event listeners when they’re no longer needed — something often overlooked, yet essential for writing robust and efficient code.


What Is Abort Controller

AbortController is part of the DOM and provides a mechanism to abort asynchronous operations such as fetch() calls or DOM event listeners.

A controller instance produces an AbortSignal, which can be passed to supported operations. When controller.abort() is called, every task listening to that signal stops immediately.

Key Properties and Methods


Practical Use Cases in React

Let’s look at some common and practical ways to use AbortController within React components.

1. Abort Fetch Requests

a) Manual aborting

export default function Users() {
    useEffect(() => {
        const controller = new AbortController()

        fetch("https://jsonplaceholder.typicode.com/users", {
            signal: controller.signal,
        })
            .then((res) => res.json())
            .then((data) => console.log(data))
            .catch((err) => {
                if (err.name === "AbortError") {
                    console.log("Request aborted")
                } else {
                    console.error(err)
                }
            })

        // Cancel the request when the component unmounts
        return () => controller.abort()
    }, [])

    return <p>Loading users...</p>
}

b) Automatic aborting with timeout

export default function Posts() {
    useEffect(() => {
        const signal = AbortSignal.timeout(2000) // aborts after 2 seconds

        fetch("https://jsonplaceholder.typicode.com/posts", { signal })
            .then((res) => res.json())
            .then((data) => console.log(data))
            .catch((err) => {
                if (err.name === "TimeoutError") {
                    console.log("Request aborted due to timeout")
                }
            })
    }, [])

    return <p>Fetching posts...</p>
}

This pattern is especially useful to prevent unnecessary network calls when a user navigates away or when a response takes too long.


2. Cleaning Up DOM Event Listeners

AbortController can also handle DOM event listeners, making cleanup in React much simpler and safer.

export default function ResizeLogger() {
    useEffect(() => {
        const controller = new AbortController()

        window.addEventListener("resize", () => console.log("Window resized"), {
            signal: controller.signal,
        })

        // Remove the event listener when the component unmounts
        return () => controller.abort()
    }, [])

    return <p>Try resizing the window.</p>
}

With this approach, any event linked to the same signal is automatically removed when abort() is called — no need for manual cleanup.


3. Aborting Multiple Signals at Once

One of the most underrated features of AbortController is that a single controller can manage multiple async operations simultaneously.

export default function MultipleAbortExample() {
    useEffect(() => {
        const controller = new AbortController()

        const fetchUsers = fetch("https://jsonplaceholder.typicode.com/users", {
            signal: controller.signal,
        })
        const fetchPosts = fetch("https://jsonplaceholder.typicode.com/posts", {
            signal: controller.signal,
        })

        Promise.allSettled([fetchUsers, fetchPosts])
            .then((results) => console.log(results))
            .catch((err) => console.error(err))

        // Abort both requests after 1.5 seconds
        setTimeout(() => controller.abort(), 1500)
    }, [])

    return <p>Aborting multiple requests in parallel...</p>
}

This is perfect for parallel data fetching or composite effects, where several operations should be canceled together (for instance, when switching users or views).


Summary

AbortController offers fine-grained control over async operations, making your code cleaner, safer, and easier to maintain.

In React, it enables you to cancel network requests, clean up event listeners, or coordinate multiple aborts elegantly — all with just a few lines of code.

By integrating it into your workflow, you’ll prevent memory leaks, improve performance, and deliver a more reliable user experience.


References