Learn JavaScript

abort() - отмена операции через AbortController

abort() - это метод объекта AbortController, который отправляет сигнал отмены.

Чаще всего его используют, чтобы отменить fetch():

const controller = new AbortController();

fetch("https://learn-javascript.ru/api/users", {
  signal: controller.signal,
});

controller.abort();

Сам abort() не принимает участие в запросе напрямую. Он меняет состояние controller.signal, а операции, которые получили этот signal, реагируют на отмену.

Синтаксис

controller.abort()

Также можно передать причину отмены:

controller.abort(reason)

Где reason - любое значение, которое объясняет, почему операция была отменена.

Самый частый вариант - вызов без аргументов.

const controller = new AbortController();

controller.abort();

Метод возвращает undefined.

const result = controller.abort();

console.log(result); // undefined

Что происходит после abort()

У AbortController есть свойство signal.

До отмены signal.aborted равен false.

const controller = new AbortController();

console.log(controller.signal.aborted); // false

После вызова abort() значение меняется на true.

controller.abort();

console.log(controller.signal.aborted); // true

Это состояние уже не откатывается обратно. Если controller отменён, он остаётся отменённым.

abort() вместе с fetch()

Чтобы abort() мог отменить fetch(), нужно передать signal в настройки запроса.

const controller = new AbortController();

const promise = fetch("https://learn-javascript.ru/api/lessons", {
  signal: controller.signal,
});

controller.abort();

Когда fetch() получает сигнал отмены, его промис отклоняется.

Обычно отмену обрабатывают через try...catch.

const controller = new AbortController();

try {
  const response = await fetch("https://learn-javascript.ru/api/lessons", {
    signal: controller.signal,
  });

  const lessons = await response.json();

  console.log(lessons);
} catch (error) {
  if (error.name === "AbortError") {
    console.log("Запрос отменён");
  } else {
    console.log("Не удалось загрузить данные");
  }
}

Если abort() вызван без своей причины, fetch() обычно попадает в catch с ошибкой AbortError.

Отмена по кнопке

Практический пример - кнопка отмены загрузки.

const controller = new AbortController();
const cancelButton = document.querySelector(".cancel-button");

cancelButton.addEventListener("click", () => {
  controller.abort();
});

try {
  const response = await fetch("https://learn-javascript.ru/api/articles", {
    signal: controller.signal,
  });

  const articles = await response.json();

  console.log(articles);
} catch (error) {
  if (error.name === "AbortError") {
    console.log("Пользователь отменил загрузку");
  }
}

Здесь abort() вызывается только в момент клика.

Причина отмены

В abort() можно передать причину отмены.

const controller = new AbortController();

controller.abort("Пользователь закрыл окно");

console.log(controller.signal.aborted); // true
console.log(controller.signal.reason); // "Пользователь закрыл окно"

Это полезно, если нужно различать разные причины отмены внутри своего кода.

const controller = new AbortController();

controller.abort({
  type: "timeout",
  message: "Запрос выполнялся слишком долго",
});

console.log(controller.signal.reason.type); // "timeout"

Для обычной отмены fetch() чаще достаточно abort() без аргументов и проверки AbortError.

abort() для таймаута

fetch() не имеет настройки timeout, поэтому таймаут часто делают через setTimeout() и abort().

async function loadWithTimeout() {
  const controller = new AbortController();

  const timeoutId = setTimeout(() => {
    controller.abort();
  }, 3000);

  try {
    const response = await fetch("https://learn-javascript.ru/api/search", {
      signal: controller.signal,
    });

    return await response.json();
  } finally {
    clearTimeout(timeoutId);
  }
}

Если запрос завершится раньше, таймер будет очищен в finally.

Если запрос не успеет завершиться за 3 секунды, сработает abort().

Повторный вызов abort()

abort() можно вызвать несколько раз, но состояние уже не изменится повторно.

const controller = new AbortController();

controller.abort();
controller.abort();

console.log(controller.signal.aborted); // true

Первый вызов переводит signal в состояние отмены. Последующие вызовы не делают controller “более отменённым”.

Отменённый controller нельзя использовать заново

После abort() тот же signal уже отменён навсегда.

const controller = new AbortController();

controller.abort();

fetch("https://learn-javascript.ru/api/users", {
  signal: controller.signal,
}); // запрос сразу будет отменён

Для нового запроса нужно создать новый AbortController.

const controller = new AbortController();

fetch("https://learn-javascript.ru/api/users", {
  signal: controller.signal,
});

Это особенно важно в поиске, автодополнении и интерфейсах, где запросы запускаются много раз.

abort() не заменяет обработку ошибок

Отмена запроса - это не единственная возможная проблема.

try {
  const response = await fetch("https://learn-javascript.ru/api/users", {
    signal: controller.signal,
  });

  if (!response.ok) {
    throw new Error(`Ошибка HTTP: ${response.status}`);
  }
} catch (error) {
  if (error.name === "AbortError") {
    console.log("Запрос отменён");
  } else {
    console.log("Другая ошибка");
  }
}

abort() отвечает только за отмену. HTTP-статусы, сетевые ошибки и ошибки чтения данных всё равно нужно обрабатывать отдельно.

Частые ошибки

Вызвать abort(), но не передать signal

const controller = new AbortController();

fetch("https://learn-javascript.ru/api/users");

controller.abort();

Такой вызов не отменит запрос, потому что fetch() не связан с controller.signal.

Правильно:

fetch("https://learn-javascript.ru/api/users", {
  signal: controller.signal,
});

Использовать controller после отмены

const controller = new AbortController();

controller.abort();

console.log(controller.signal.aborted); // true

После этого для новой операции нужен новый AbortController.

Показывать ошибку пользователю при обычной отмене

Если пользователь сам нажал “Отмена”, это не всегда ошибка интерфейса.

catch (error) {
  if (error.name === "AbortError") {
    return;
  }

  console.log("Не удалось загрузить данные");
}

Так можно тихо обработать ожидаемую отмену и не показывать лишнее сообщение.

Быстрая памятка

  • abort() вызывают у объекта AbortController
  • метод отправляет сигнал отмены через controller.signal
  • после вызова signal.aborted становится true
  • abort() возвращает undefined
  • чтобы отменить fetch(), нужно передать signal в настройки запроса
  • отменённый fetch() обычно попадает в catch с AbortError
  • в abort(reason) можно передать причину отмены
  • после abort() controller нельзя использовать для нового запроса
  • для нового запроса создавайте новый AbortController
  • abort() не заменяет проверку HTTP-ошибок и обработку других ошибок

Коротко

abort() - это метод AbortController, который переводит signal в состояние отмены. Если этот signal был передан в fetch(), запрос будет отменён, а промис попадёт в catch. После вызова abort() controller нельзя использовать повторно: для новой операции создавайте новый AbortController.