AbortController in web and how it can improve your app
Tags: javascript, webdev, react, fetch
Published: 2022-11-09 19:57:29

Hi guys! Today I am going to tell you what is `AbortController`, why you possible need it and how it can improve the performance of your application.

## Introduction

In a small IT chat, I conducted a survey and it turned out that only 9% of web developers used it in a real project, and 64% of web developers do not know what it is at all.

So let's get started!

The `AbortController` interface represents a controller object that allows you to abort one or more Web requests as and when desired.

This is a very simple interface that consists of the `signal` property and the `abort` method

```
const controller = new AbortController();
const signal = controller.signal;
const abort = controller.abort;
```

We pass `signal` to the call method: `fetch(url, { signal })`
and with `abort()` this call can be stopped

## Examples

### Fetch

This simple code our application:
```
export default function App() {
  const [url, setUrl] = useState("users");
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true);
    fetch(`https://jsonplaceholder.typicode.com/${url}`)
      .then((res) => res.json())
      .then((res) => {
        setData(res);
        setLoading(false);
      });
  }, [url]);

  return (
    <div className="App">
      <div className="nav">
        <Link to="/users" onClick={() => setUrl("users")}>
          users
        </Link>
        <Link to="/todos" onClick={() => setUrl("todos")}>
          todos
        </Link>
        <Link to="/posts" onClick={() => setUrl("posts")}>
          posts
        </Link>
      </div>
      <div className="page">
        <Switch>
          <Route path="/users" component={() => <div>Users page</div>} />
          <Route path="/todos" component={() => <div>Todos page</div>} />
          <Route path="/posts" component={() => <div>Posts page</div>} />
        </Switch>

        {loading ? (
          "loading..."
        ) : (
          <ul>
            {data?.splice(0, 5).map((item) => (
              <li key={item.id}>{item?.name || item?.title}</li>
            ))}
          </ul>
        )}
      </div>
    </div>
  );
}
```

and it works like this:

![app without AbortController](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6ob4b6io6iwgwvx68za.gif)

As you can see all 3 requests reach the target and are displayed on the screen in turn

We can imagine a situation where the user navigates through our menu faster than the requests have time to be processed on the server, that mean we do unnecessary requests

For example: the user come to the "/user" page, a request has begun, next user come to the "/post" page, at this moment we assume that he no longer needs the "/user" page, and this request can be canceled and not load the server

This is how the code looks like with AbortController, I changed only useEffect:

```
  useEffect(() => {
    const controller = new AbortController();
    const signal = controller.signal;

    setLoading(true);    
    fetch(`https://jsonplaceholder.typicode.com/${url}`, { signal })
      .then((res) => res.json())
      .then((res) => {
        setData(res);
        setLoading(false);
      });

    return () => {
      controller.abort();
    };
  }, [url]);
```
and that's what happened:

![app with AbortController](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ljipuknf6dexoae5di4g.gif)

Unnecessary requests have been cancelled!

![canceled request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ag8ixrqbpo0eqavw1y4b.png)

This is the main point of performance!

### Axios

identical with fetch

```
const controller = new AbortController();
axios.get('/user', { signal: controller.signal })
```

### GQL

GQL have almost the same way, but you need to use `useRef`

In the following example we cancel the request when we leave the page:

```
const controllerRef = useRef(new AbortController());

const [getUser, { data }] = useLazyQuery(USER_QUERY, {
  context: {
    fetchOptions: {
      signal: controllerRef.current.signal,
    },
  },
});

useEffect(() => {
  return () => controllerRef.current.abort();
}, []);
```

## Conclusion

AbortController is a simple and effective way to improve performance which you can try in your projects, good luck!