Как мне отменить запрос HTTP fetch()?

Существует новый API для запросов от JavaScript: fetch(). Есть ли встроенный механизм для отмены этих запросов в полете?

Ответ 1

TL/DR:

fetch теперь поддерживает параметр signal состоянию на 20 сентября 2017 года, но, похоже, не все браузеры поддерживают этот atm.

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

Длинная версия

Как:

Вот как это работает:

Шаг 1: Вы создаете AbortController (сейчас я только что использовал это)

const controller = new AbortController()

Шаг 2: Вы получаете сигнал AbortController подобный этому:

const signal = controller.signal

Шаг 3: Вы передаете signal для извлечения следующим образом:

fetch(urlToFetch, {
    method: 'get',
    signal: signal, // <------ This is our AbortSignal
})

Шаг 4: Просто прерывайте всякий раз, когда вам нужно:

controller.abort();

Вот пример того, как это будет работать (работает в Firefox 57+):

<script>
    // Create an instance.
    const controller = new AbortController()
    const signal = controller.signal

    /*
    // Register a listenr.
    signal.addEventListener("abort", () => {
        console.log("aborted!")
    })
    */


    function beginFetching() {
        console.log('Now fetching');
        var urlToFetch = "https://httpbin.org/delay/3";

        fetch(urlToFetch, {
                method: 'get',
                signal: signal,
            })
            .then(function(response) {
                console.log('Fetch complete. (Not aborted)');
            }).catch(function(err) {
                console.error(' Err: ${err}');
            });
    }


    function abortFetching() {
        console.log('Now aborting');
        // Abort.
        controller.abort()
    }

</script>



<h1>Example of fetch abort</h1>
<hr>
<button onclick="beginFetching();">
    Begin
</button>
<button onclick="abortFetching();">
    Abort
</button>

Ответ 2

Я не верю, что есть способ отменить запрос с существующим API-интерфейсом. Продолжается обсуждение этого вопроса на https://github.com/whatwg/fetch/issues/27

Обновление до 2017 года: разрешение по-прежнему отсутствует. Запросы не могут быть отменены. Больше обсуждений на https://github.com/whatwg/fetch/issues/447

Ответ 3

https://developers.google.com/web/updates/2017/09/abortable-fetch

настроить:

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

fetch(url, { signal }).then(response => {
  return response.text();
}).then(text => {
  console.log(text);
});

прервать: controller.abort()

работает в версии 16 (2017-10-17), Firefox 57 (2017-11-14), настольное Safari 11.1 (2018-03-29), IOS Safari 11.4 (2018-03-29), Chrome 67 (2018-05 -29) и позже.

Ответ 4

По состоянию на февраль 2018 года fetch() можно отменить с помощью приведенного ниже кода в Chrome (см. " Использование читаемых потоков", чтобы включить поддержку Firefox). Для catch() не AbortController никакой ошибки, и это временное решение, пока AbortController будет полностью принят.

fetch('YOUR_CUSTOM_URL')
.then(response => {
  if (!response.body) {
    console.warn("ReadableStream is not yet supported in this browser.  See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream")
    return response;
  }

  // get reference to ReadableStream so we can cancel/abort this fetch request.
  const responseReader = response.body.getReader();
  startAbortSimulation(responseReader);

  // Return a new Response object that implements a custom reader.
  return new Response(new ReadableStream(new ReadableStreamConfig(responseReader)));
})
.then(response => response.blob())
.then(data => console.log('Download ended. Bytes downloaded:', data.size))
.catch(error => console.error('Error during fetch()', error))


// Here an example of how to abort request once fetch() starts
function startAbortSimulation(responseReader) {
  // abort fetch() after 50ms
  setTimeout(function() {
    console.log('aborting fetch()...');
    responseReader.cancel()
    .then(function() {
      console.log('fetch() aborted');
    })
  },50)
}


// ReadableStream constructor requires custom implementation of start() method
function ReadableStreamConfig(reader) {
  return {
    start(controller) {
      read();
      function read() {
        reader.read().then(({done,value}) => {
          if (done) {
            controller.close();
            return;
          }
          controller.enqueue(value);
          read();
        })
      }
    }
  }
}

Ответ 5

Пока нет правильного решения, как говорит @spro.

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

fetch('http://example.com').then((res) => {
  const reader = res.body.getReader();

  /*
   * Your code for reading streams goes here
   */

  // To abort/cancel HTTP request...
  reader.cancel();
});