Home > OS >  AbortController.abort(reason), but the reason gets lost before it arrives to the fetch catch clause
AbortController.abort(reason), but the reason gets lost before it arrives to the fetch catch clause

Time:07-20

I am implementing abortable fetch calls.

There are basically two reasons for aborting the fetch on my page:

  • the user decides he/she does not want to wait for the AJAX data anymore and clicks a button; in this case the UI shows a message "call /whatever interrupted"
  • the user has moved to another part of the page and the data being fetched are no longer needed; in this case I don't want the UI to show anything, as it'd just confuse the user

In order to discriminate the two cases I was planning to use the reason parameter of the AbortController.abort method, but the .catch clause in my fetch call always receives a DOMException('The user aborted a request', ABORT_ERROR).

I have tried to provide a different DOMException as reason for the abort in case 2, but the difference is lost.

Has anyone found how to send information to the fetch .catch clause with regards to the reason to abort?

CodePudding user response:

In the example below, I demonstrate how to determine the reason for an abortion of a fetch request. I provide inline comments for explanation. Feel free to comment if anything is unclear.

Re-run the code snippet to see a (potentially different) random result

'use strict';

function delay (ms, value) {
  return new Promise(res => setTimeout(() => res(value), ms));
}

function getRandomInt (min = 0, max = 1) {
  return Math.floor(Math.random() * (max - min   1))   min;
}

// Forward the AbortSignal to fetch:
// https://docs.github.com/en/rest/repos/repos#list-public-repositories
function fetchPublicGHRepos (signal) {
  const headers = new Headers([['accept', 'application/vnd.github json']]);
  return fetch('https://api.github.com/repositories', {headers, signal});
}

function example () {
  const ac = new AbortController();
  const {signal} = ac;

  const abortWithReason = (reason) => delay(getRandomInt(1, 5))
    .then(() => {
      console.log(`Aborting ${signal.aborted ? 'again ' : ''}(reason: ${reason})`);
      ac.abort(reason);
    });

  // Unless GitHub invests HEAVILY into our internet infrastructure,
  // one of these promises will resolve before the fetch request
  abortWithReason('Reason A');
  abortWithReason('Reason B');

  fetchPublicGHRepos(signal)
    .then(res => console.log(`Fetch succeeded with status: ${res.status}`))
    .catch(ex => {
      // This is how you can determine if the exception was due to abortion
      if (signal.aborted) {
        // This is set by the promise which resolved first
        // and caused the fetch to abort
        const {reason} = signal;
        // Use it to guide your logic...
        console.log(`Fetch aborted with reason: ${reason}`);
      }
      else console.log(`Fetch failed with exception: ${ex}`);
    });

  delay(10).then(() => console.log(`Signal reason: ${signal.reason}`));
}

example();

  • Related