Imagine a scenario where you make an asynchronous request in JavaScript that takes a long time and doesn’t return, and you want to interrupt the request. How do you do that?

Fortunately, JavaScript provides a convenient way to interrupt an asynchronous task. In this article, you will be shown how to use it to create your own interruptible methods.

Abort Signal

With the introduction of Promise in ES2015 and the emergence of better Web apis that support asynchronous solutions, there was a need to eliminate asynchronous tasks.

Initial attempts were aimed at creating a common solution that could have been part of the ECMAScript standard. However, discussions soon bogged down and could not resolve the issue.

Therefore, the WHATWG prepared its own solution and introduced it directly into the DOM in the form of an AbortController. The downside of this solution is that AbortController is not provided in Node.js, so there is no elegant or official way to cancel asynchronous tasks in the Node environment.

As you can see in the DOM specification, AbortController is described in a very general way. Therefore, you can use it in any kind of asynchronous API — even one that doesn’t yet exist. Currently only the Fetch API officially supports it, but that doesn’t stop you from using it in your own code!

How does AbortController work?

First, let’s examine how AbortController works:

const abortController = new AbortController() / / 1

const abortSignal = abortController.signal    / / 2



fetch('http://example.com', {

  signal: abortSignal       / / 3

}).catch(({ message }) = > { / / 5

  console.log(message)

})



abortController.abort()     / / 4

Copy the code
  1. First, create an instance of the AbortController DOM interface.
  2. Place the instance’sSignal propertiesBind to a variable;
  3. Execute the fetch() method with signal as its options;
  4. callabortController.abort()Method to interrupt the fetch() execution;
  5. At this point, the control flow is passed to the Catch () block.

AbortController Indicates the signal property of the instance

In fact, the signal attribute is an instance of the AbortSignal DOM interface, which has an aborted attribute that indicates whether the user has called the abortController.Abort () method.

So you can bind an abort listener to signal. Abortcontroller.abort () fires when abortController.abort() is called.

In other words, AbortController is just a public interface to AbortSignal.

Abortable function Abortable function

Suppose you have an asynchronous function that needs to do a complex operation (for example, it needs to process a large array of data asynchronously). For simplicity, the following example simulates a complex operation to wait five seconds before returning the result.

function calculate({

  return new Promise((resolve, reject) = > {

    setTimeout((a)= > {

      resolve(1)

    }, 5000)

  })

}



calculate().then((result) = > {

  console.log(result)

})

Copy the code

However, sometimes the user needs to interrupt such a time-consuming operation:

  • Add a button that can be clicked to interrupt;
  • Added the ability to interrupt asynchronous tasks.
<button id="calculate">Calculate</button>



<script type="module">

  { / / 1

    let abortController = null  / / 2



    document.querySelector('#calculate').addEventListener('click'.async ({ target }) => {

      if (abortController) {

        abortController.abort() / / 5

        abortController = null

        target.innerText = 'Calculate'

        return

      }



      abortController = new AbortController() / / 3

      target.innerText = 'Stop calculation'



      try {

        const result = await calculate(abortController.signal)  / / 4

        alert(result)

      } catch {

        alert('WHY DID YOU DO THAT? ! ')  / / 9

      } finally { / / 10

        abortController = null

        target.innerText = 'Calculate'

      }

    })



    function calculate(abortSignal{

      return new Promise((resolve, reject) = > {

        const error = new DOMException( 'Calculation aborted by the user'.'AbortError' )



        Abortcontroller.abort () is called before abortSignal is passed in

        if (abortSignal.aborted) {

          return reject(error)

        }



        const timeout = setTimeout((a)= > {

          resolve(1)

        }, 5000)



        abortSignal.addEventListener('abort', () = > {/ / 6

          clearTimeout(timeout) / / 7

          reject(error) / / 8

        })

      })

    }

  }

</script>

Copy the code

Let’s examine the above code:

  1. All of the code is contained in a single code block, which is equivalent to an IIFE immediately executing function;
  2. defineAbortController variableAnd initialize tonullBecause it is inside the code block, the abortController is not leaked to the global scope;
  3. Click on the button to create a new oneAbortController instanceAnd assign to the abortController variable;
  4. AbortController instanceSignal propertiesPass to the calculate() function;
  5. If the button is clicked again within five seconds, it will triggerabortController.abort();
  6. Because within Calculate (),AbortSignal listens for the ABORT event, so the event is triggered;
  7. Within the ABORT event handler, remove the timer and the asynchronous task is interrupted.
  8. throwDOMExceptionExceptions (according to the specification,AbortErrorThe type must be DOMException.

So just to conclude

  • Using the interrupt asynchronous task function can reduce unnecessary requests and improve page performance.
  • With interruptible functions, we have the medicine of regret to eat! O ha ha ~ O (studying studying)

How’s that? Very interesting! Hurry up and play in your project!

This article is published in my wechat public number, welcome to pay attention to, continue to update!

Front-end sauce daily wechat public qr code

This article is formatted using MDNICE