DEV Community

Cover image for Terminating DOM Operations at will: 'AbortController' in JavaScript
Abdus Azad
Abdus Azad

Posted on • Originally published at abdus.xyz on

Terminating DOM Operations at will: 'AbortController' in JavaScript

AbortController is an interface which provides a way for terminating one or
more web request as and when desired.

This generally means that a request can be terminated by a user whenever needed,
irrespective of whether the operation is finished or not.
AbortSignal can be implemented in any web platform API which uses
Promise.

The API

AbortController provides a few things for users to implement it effectively in
code. At the time of writing this, the constructor would return an instance which
contains a method AbortController.abort() and a property
AbortController.signal(read-only).

  • AbortController.signal - Returns a AbortSignal instance
  • AbortController.abort() - Aborts a DOM Request

AbortSignal

AbortController.signal returns an instance of type AbortSignal which
represents the current state of the AbortController instance.
It has a read-only property AbortSignal.aborted. Type of property aborted
is Boolean.

AbortSignal is also responsible for communicating with DOM as an Event Listener
can be attached to it.

Using AbortController

To use AbortController in a Promise, one must adhere to a few rules.
Source

  • Function should accept AbortSignal through a signal property. For example:
  function abortThis(arg1, { signal }) {
    return new Promise((resolve, reject) => {
      // function body
    })
  }
Enter fullscreen mode Exit fullscreen mode
  • When method aborted() is called, reject the Promise with a DOMException AbortError
  throw new DOMException('aborted by user', 'ABORT_ERR');
Enter fullscreen mode Exit fullscreen mode
  • Abort immediately if the aborted flag on AbortSignal is set to true.

Browser Support

Despite being a relatively new API, browser support for AbortController is
quite awesome! All major browser fully supports it. Following is a chart
from Can I Use.

<div
class="ciu_embed"
data-feature="abortcontroller"
data-periods="future_1,current,past_1,past_2"
data-accessible-colours="false"


Data on support for the abortcontroller feature across the major
browsers


Examples

Following are a few examples of AbortController API. First one is using fetch
API. On the second example, I will try to implement it in a Promise.

Using Fetch API

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

// fetch a URL
fetch('https://jsonplaceholder.typicode.com/todos/1', { signal })
  .then(raw => raw.ok && raw.json())
  .then(console.log)
  .catch(e => console.log(e.message));

// driver code
setTimeout(() => {
  // abort network request if it takes
  // more than a second
  controller.abort();
}, 500);
Enter fullscreen mode Exit fullscreen mode

The above code allows a network request to run for 500ms. If data has been
fetched within that period, it would return JSON representation of the response.
Otherwise, it would abort the request.

Implementing using Promise

In this section, I will try to implement AbortController for a function which
returns Promise. Should be quite easy and straightforward.

/* this function would return a Promise
 * which would resolve with value `hello`
 * after 4s
 */
function abortTask({ signal }) {
  return new Promise((resolve, reject) => {
    // 1. check if it's already aborted
    if (signal && signal.aborted) {
      return reject(new DOMException('`signal` is in `aborted` state', 'ABORT_ERR'));
    }

    // wait for 4s
    setTimeout(() => resolve("hello"), 4000);

    // 2. add a listener to `signal` to check its state
    signal && signal.addEventListener('abort', () => {
      // reject promise when signal.aborted changes to `true`
      return reject(new DOMException('aborted by user', 'ABORT_ERR'));
    })
  })
}



/* DRIVER CODE */
let signal;   // just so that I could re-use it


// when `signal.aborted` is `false`
const abortController_1 = new AbortController();
signal = abortController_1.signal;

abortTask({ signal })
  .then(t => console.log(t)) // hello
  .catch(e => console.error(e.message))


// when `signal.aborted` is `true`
const abortController_2 = new AbortController();
signal = abortController_2.signal;

abortController_2.abort();

abortTask({ signal })
  .then(t => console.log(t))
  .catch(e => console.error(e.message)) // err


// when user calls AbortController.abort()
const abortController_3 = new AbortController();
signal = abortController_3.signal;

// abort task in 2s
setTimeout(() => abortController_3.abort(), 2000);

abortTask({ signal })
  .then(t => console.log(t))
  .catch(e => console.error(e.message)) // err
Enter fullscreen mode Exit fullscreen mode

Top comments (0)