How to cancel Javascript API request with AbortController
Tags: javascript, webdev, frontend, html
Published: 2022-04-09 06:33:04

The **[fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)** API is a JavaScript API for making HTTP requests. It is a replacement for XMLHttpRequest, which is deprecated in most browsers. It is a promise-based API, meaning that it returns a Promise object that is resolved or rejected when the request is complete.

##### Let's take a look at the fetch() function.

```javascript
fetch(url)
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((error) => console.error(error));
```

##### This is great but sometimes we want to cancel the already initiated request.

Let's take a look at the following example.
Suppose we have search input we want to show the suggestions as the user types. when the user types a character, we call the fetch() function and pass the value of the search input field as the URL parameter.

```javascript
fetch(`${url}?q=${searchInput.value}`)
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((error) => console.error(error));
```

#### Problem:

The fetch() function is called every time the user types a character, This is an inefficient approach as it is called multiple times and we are only interested in the last response.

#### Solution:

Use **debounce()** function to limit the number of times the **fetch()** function is called. It will only be called after the user has stopped typing for a certain period (100ms). This is a good practice to avoid unnecessary calls to the API.

**Edge case:** What if the user starts typing just after debounce() has been called. In that case, the fetch() function will be called again. It's better than the previous approach.

**How can we handle this?**

This is where **[AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)** comes in handy. We can use AbortController to cancel the already initiated request(using fetch). The modern browsers come with a built-in AbortController interface.

You can create a new **[AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)** object using the AbortController() constructor.

#### Properties:

###### [AbortController.signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal):

Returns an AbortSignal object instance, which can be used to communicate with, or to abort, a DOM request.

#### Method:

###### [AbortController.abort()](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort):

Aborts a DOM request before it has been completed. When we abort an async operation, the promise rejects with a DOMException named AbortError.

---

#### A real-world example

I'm not going to create debounce() function here, but let's take a look at the following example.

```html
<!DOCTYPE html>
<html>
  <body>
    <input id="search" type="number" />
    <script>
      const results = [];
      const search = document.getElementById("search");
      let controller = new AbortController();
      let signal = controller.signal;

      const getPost = async (value, signal) => {
        try {
          const response = await fetch(
            `https://jsonplaceholder.typicode.com/posts/${value}`,
            { signal }
          );
          results.push(`Success: ${value}`);
        } catch (error) {
          if (error.name === "AbortError") {
            results.push("API failure");
          } else {
            console.log("Some other error");
          }
        } finally {
          console.log("Status", results);
        }
      };
      const onChange = () => {
        const value = search.value;
        if (value) {
          controller.abort();
          controller = new AbortController();
          signal = controller.signal;
          getPost(value, signal);
        }
      };
      search.onkeyup = onChange;
    </script>
  </body>
</html>
```

#### React example

```javascript
useEffect(() => {
  const controller = new AbortController();
  const signal = controller.signal;
  const results = [];
  const getPost = async (value) => {
    try {
      const response = await fetch(
        `https://jsonplaceholder.typicode.com/posts/${value}`,
        { signal }
      );
      results.push(`Success: ${value}`);
    } catch (error) {
      if (error.name === "AbortError") {
        results.push("API failure");
      }
    } finally {
      console.log("Status", results);
    }
  };

  getPost(1);

  return () => {
    // Cancel the request on unmount
    controller.abort();
  };
}, []);
```

> ###### Note: Better approach could be combining debounce() and AbortController.

---

### Must Read If you haven't

{% link https://dev.to/devsmitra/getting-started-with-solidjs-a-beginners-guide-5af4 %}
{% link https://dev.to/devsmitra/useasync-hook-with-cache-5817 %}
{% link https://dev.to/devsmitra/javascript-short-reusable-functions-everyone-will-always-need-22oc %}
{% link https://dev.to/devsmitra/13-typescript-utility-a-cheat-sheet-for-developer-ab3 %}

### Catch me on
{% user devsmitra %}
<p>
{% cta https://www.youtube.com/@bit-buddy?sub_confirmation=1 %} Youtube {% endcta %}
{% cta https://github.com/devsmitra %} Github {% endcta %}
{% cta https://www.linkedin.com/in/rahul-sharma-18b8b374 %} LinkedIn {% endcta %}
{% cta https://devsmitra.medium.com %} Medium {% endcta %}
{% cta https://stackblitz.com/@devsmitra %} Stackblitz {% endcta %} {% cta https://devsmitra.hashnode.dev %} Hashnode {% endcta %}
{% cta https://hackernoon.com/u/devsmitra %} HackerNoon {% endcta %}
</p>