Як скасувати запит на отримання HTTP fetch ()?


Відповіді:


281

TL / DR:

fetchтепер підтримує signalпараметр станом на 20 вересня 2017 року, але, здається , не всі браузери підтримують це на даний момент .

ОНОВЛЕННЯ 2020: Більшість основних браузерів (Edge, Firefox, Chrome, Safari, Opera та деякі інші) підтримують цю функцію , яка стала частиною життєвого рівня DOM . (станом на 5 березня 2020 року)

Це зміна, яку ми побачимо дуже скоро, тому вам слід мати можливість скасувати запит, використовуючи AbortControllers AbortSignal.

Довга версія

Як:

Це працює так:

Крок 1 : Ви створюєте AbortController(на даний момент я лише використовував це )

const controller = new AbortController()

Крок 2 : Ви отримуєте AbortControllerсигнал s таким чином:

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>

Джерела:

  • Кінцева версія AbortController була додана до специфікації DOM
  • Відповідний PR для вибірки специфікації тепер об'єднані.
  • Помилки браузера, які відстежують реалізацію AbortController, доступні тут: Firefox: # 1378342 , Chromium: # 750599 , WebKit: # 174980 , Edge: # 13009916 .

2
Ця відповідь є правильною і її слід схвалювати. Але я взяв на себе змогу внести зміни в фрагмент коду, тому що, як-от він насправді не працює у Firefox 57+ - схоже, що shim викликає його збій ( "Err: TypeError: 'signal", член RequestInit не реалізує інтерфейс AbortSignal. " ), і, здається, є проблема з cert для slowwly.robertomurray.co.uk ( " Цей сервер не міг довести, що він є slowwly.robertomurray.co.uk; його сертифікат безпеки походить від * .herokuapp.com. ” ), тож я змінив його просто на slowwly.robertomurray.co.uk (звичайний http).
sidehowbarker

3
Але зараз це не працює в інших браузерах, тобто Chrome AbortController is not defined. Так чи інакше, це лише доказ концепції, принаймні люди з Firefox 57+ можуть побачити, як це працює
SudoPlz

3
Це чисте золото StackOverflow, дякую за стисле написання! І потворні посилання також!
Kjellski

3
Зараз це підтримують усі сучасні браузери. developer.mozilla.org/en-US/docs/Web/API/AbortController/abort див. таблицю внизу
Олексій Івасюв

2
Дякую, але у мене все ще виникає питання, чи слід змінити сигнал назад на справжній для наступного вибору вручну ??
акшай кишор

20

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

https://dom.spec.whatwg.org/#aborting-ongoing-activities

// setup AbortController
const controller = new AbortController();
// signal to pass to fetch
const signal = controller.signal;

// fetch as usual
fetch(url, { signal }).then(response => {
  ...
}).catch(e => {
  // catch the abort if you like
  if (e.name === 'AbortError') {
    ...
  }
});

// when you want to abort
controller.abort();

працює в краю 16 (2017-10-17), firefox 57 (2017-11-14), сафарі на робочому столі 11.1 (2018-03-29), ios safari 11.4 (2018-03-29), chrome 67 (2018-05 -29), а пізніше.


у старих веб-переглядачах можна використовувати поліфункцію Whatwg-fetch від github та поліфункцію AbortController . ви можете виявляти старі веб-переглядачі та використовувати поліфілі також умовно :

import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
import {fetch} from 'whatwg-fetch'

// use native browser implementation if it supports aborting
const abortableFetch = ('signal' in new Request('')) ? window.fetch : fetch

Якщо ви користуєтеся політом заповнення github, це можливо зробити з цим, просто дотримуйтесь інструкцій з їхнього readme: github.com/github/fetch#aborting-requests
Fábio Santos

@ FábioSantos Чи повинен ваш коментар бути на запитання чи як відповідь у власній формі? На мою відповідь це не виглядає конкретно.
Jayen

Просто примітка для людей, які використовують поліфункцію github fetch. Я подумав, що це відповідає вашій відповіді, оскільки AFAIK - це найпопулярніша доступна поліфазова заповнення, і вона заповнює функцію, яку ви використовуєте. Багато людей будуть використовувати цю поліфункцію через старі браузери. Мені було важливо згадати, оскільки люди просто припускають, що поліфіли все виправляють, але саме цей не намагається переповнити AbortController. Вони спробують використати AbortController, думаючи, що це буде заповнено в старих браузерах, і бум, є виняток у кутовому випадку та лише у старих браузерах.
Фабіо Сантос

5

З лютого 2018 року fetch()його можна скасувати за допомогою наведеного нижче коду на Chrome (читайте Використання читаних потоків для ввімкнення підтримки Firefox). Жодна помилка не приймається catch(), і це тимчасове рішення до 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's 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();
        })
      }
    }
  }
}

2
Це НЕ, про що просила ОП. Вони хочуть скасувати збір не читача. Обіцянка Fetch не вирішується, поки ПІСЛЯ запит не закінчиться, що занадто пізно, щоб скасувати запит на сервер.
Рахлі

3

Наразі немає відповідного рішення, як стверджує @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();
});

0

Давайте поліфілюємо:

if(!AbortController){
  class AbortController {
    constructor() {
      this.aborted = false;
      this.signal = this.signal.bind(this);
    }
    signal(abortFn, scope) {
      if (this.aborted) {
        abortFn.apply(scope, { name: 'AbortError' });
        this.aborted = false;
      } else {
        this.abortFn = abortFn.bind(scope);
      }
    }
    abort() {
      if (this.abortFn) {
        this.abortFn({ reason: 'canceled' });
        this.aborted = false;
      } else {
        this.aborted = true;
      }
    }
  }

  const originalFetch = window.fetch;

  const customFetch = (url, options) => {
    const { signal } = options || {};

    return new Promise((resolve, reject) => {
      if (signal) {
        signal(reject, this);
      }
      originalFetch(url, options)
        .then(resolve)
        .catch(reject);
    });
  };

  window.fetch = customFetch;
}

Майте на увазі, що код не перевіряється! Дайте мені знати, чи ви протестували це, і щось не вийшло. Це може попередити, що ви намагаєтеся замінити функцію "отримання" з офіційної бібліотеки JavaScript.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.