Отмена fetch-запроса

Перевод статьи Ganapati V S «Aborting a fetch request»

Используйте AbortController для отмены запроса.

В настоящее время существует два основных способа совершения запросов: XMLHttpRequest и fetch. XMLHttpRequest существует уже долгое время, а fetch был добавлен в ES6.

У XMLHttpRequest всегда была возможность отмены запроса. И отмена этого запроса выглядит так:

let xhr = new XMLHttpRequest();
 xhr.method = 'GET';
 xhr.url = 'https://slowmo.glitch.me/5000';
 xhr.open(method, url, true);
 xhr.send();

 // Отменяем запрос по клику.
 abortButton.addEventListener('click', function() {
   xhr.abort();
 });

У fetch-запроса изначально не было способа отмены. Соответствующее ишью было открыто на гитхабе в 2015 году. Кроме того, было много попыток решить проблему без изменения спецификации. Например, отменяемые промисы и другие хаки.

Но теперь мы, наконец-то, имеет AbortController и AbortSignal API. Этот API предоставляется через DOM-стандарт, а не является частью языка.

Что такое AbortController?

Вот описание спецификации:

Хотя промисы и не имеют встроенную функцию отмены, многие API используют их. AbortController предоставляет метод abort(), который переключает состояние соответствующего объекта AbortSignal. API, которые хотят использовать прерывание, может принять объект AbortSignal и использовать его состояние, чтобы решить что делать.

// Создаём AbortController
const controller = new AbortController();
const signal = controller.signal;

// Слушает signal, коллбэк выполняет controller.abort()
signal.addEventListener('abort', () => {
  console.log(signal.aborted); // true
});

// Отменяет позднее, уведомляя signal
controller.abort();

Как отменить fetch-запрос, используя AbortController?

Fetch принимает AbortSignal как часть второго аргумента.

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

// API отвечает через 5 секунд.
// Обратите внимание: 'signal' — второй аргумент.
fetch('https://slowmo.glitch.me/5000', { signal })
  .then(r => r.json())
  .then(response => console.log(response))
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Fetch was aborted');
    } else {
      console.error('Oops!', err);
    }
  });


// Отменяет запрос через 2 секунды.
setTimeout(() => {
  controller.abort();
}, 2000);

Отмена запроса отменяет и отправку, и получение. Запрос выдаёт ошибку new DOMException(‘Aborted’, ‘AbortError’).

Тот же AbortSignal (signal в коде выше) может использоваться для отмены нескольких fetch-запросов.

AbortController работает не только с fetch-запросами. Это часть общего API и он может использоваться для отмены асинхронных задач. Например, с помощью него можно создавать отменяемые промисы.

Кроме того, заметьте, что многие старые браузеры не поддерживают AbortController. Но вы можете использовать полифилы:

Комментариев ещё нет. Оставьте первый!