Jak anulować żądanie pobierania HTTP ()?


Odpowiedzi:


281

TL / DR:

fetchobsługuje teraz signalparametr od 20 września 2017 r., ale obecnie nie wszystkie przeglądarki obsługują ten parametr .

AKTUALIZACJA 2020: Większość głównych przeglądarek (Edge, Firefox, Chrome, Safari, Opera i kilka innych) obsługuje tę funkcję , która stała się częścią standardu DOM . (od 5 marca 2020 r.)

Jest to zmiana, którą wkrótce zobaczymy, więc powinieneś być w stanie anulować żądanie za pomocą AbortControllers AbortSignal.

Długa wersja

Jak:

Sposób działania jest następujący:

Krok 1 : Tworzysz AbortController(na razie właśnie tego użyłem )

const controller = new AbortController()

Krok 2 : Otrzymasz AbortControllersygnał s w ten sposób:

const signal = controller.signal

Krok 3 : Przekazujesz signaldo pobrania tak:

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

Krok 4 : Porzuć, gdy tylko chcesz:

controller.abort();

Oto przykład jego działania (działa w przeglądarce 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>

Źródła:


2
Ta odpowiedź jest poprawna i powinna być oceniona. Pozwoliłem sobie jednak na wprowadzenie pewnych zmian we fragmencie kodu, ponieważ w rzeczywistości nie działał on w przeglądarce Firefox 57+ - wydawało się, że podkładka powodowała jego awarię ( „Err: TypeError:„ signal ”członek RequestInit nie implementuje interfejsu AbortSignal. ” ) i wydaje się, że jest jakiś problem z certyfikatem dla slowwly.robertomurray.co.uk ( „ Ten serwer nie może udowodnić, że jest slowwly.robertomurray.co.uk; jego certyfikat bezpieczeństwa pochodzi z * .herokuapp.com. ” ), więc zmieniłem go tak, by używał slowwly.robertomurray.co.uk (zwykły http).
— sideshowbarker

3
Ale teraz nie działa w innych przeglądarkach, np. Chrome, ponieważ AbortController is not defined. W każdym razie jest to tylko dowód koncepcji, przynajmniej ludzie z Firefoksem 57+ mogą zobaczyć, jak działa
— SudoPlz,

3
To jest czyste złoto StackOverflow, dzięki za zwięzłe napisanie! A także link do robota błędów!
— Kjellski

3
Teraz obsługują go wszystkie nowoczesne przeglądarki. developer.mozilla.org/en-US/docs/Web/API/AbortController/abort patrz tabela na dole
— Alex Ivasyuv

2
Dzięki, ale wciąż mam pytanie, czy powinniśmy zmienić sygnał z powrotem na true dla następnego pobierania ręcznie?
— akshay kishore,

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();

działa w Edge 16 (2017-10-17), Firefox 57 (14.11.2017), Desktop Safari 11.1 (2018-03-29), iOS Safari 11.4 (2018-03-29), Chrome 67 (2018-05 -29) i później.


w starszych przeglądarkach możesz używać wielopełniacza github whatwg-fetch i wielopełniacza AbortController . możesz wykrywać starsze przeglądarki i warunkowo korzystać z wypełniaczy :

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

Jeśli używasz polifillu pobierania github, możesz to zrobić, po prostu postępuj zgodnie z instrukcjami na ich pliku
— Fábio Santos

@ FábioSantos Czy Twój komentarz powinien dotyczyć pytania, czy stanowić osobną odpowiedź? Nie wygląda to konkretnie na moją odpowiedź.
— Jayen

Tylko uwaga dla osób, które używają polifillu pobierania github. Pomyślałem, że ma to związek z twoją odpowiedzią, ponieważ AFAIK jest najpopularniejszym dostępnym polifillem dostępnym do pobrania, i wypełnia funkcję, której używasz, pobierz. Wiele osób będzie korzystać z tego wypełniania z powodu starych przeglądarek. Uważam, że warto o tym wspomnieć, ponieważ ludzie zakładają, że polypełniacze naprawiają wszystko, ale ten konkretny nie próbuje AbortController. Spróbowaliby użyć AbortControllera, myśląc, że będzie on wypełniany w starych przeglądarkach, a boom, jest wyjątek w sprawie narożnej i tylko w starych przeglądarkach.
— Fábio Santos

5

Od lutego 2018 r. fetch()Można go anulować za pomocą poniższego kodu w Chrome (przeczytaj Korzystanie z czytelnych strumieni, aby włączyć obsługę przeglądarki Firefox). Błąd nie jest zgłaszany catch()do odebrania, a jest to rozwiązanie tymczasowe, dopóki nie AbortControllerzostanie w pełni przyjęte.

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
O to NIE prosił PO. Chcą anulować pobieranie, a nie czytnik. Obietnica Fetch nie jest rozpatrywana, dopóki PO zakończeniu żądania nie jest za późno, co jest zbyt późne, aby anulować żądanie do serwera.
— Rahly,

3

Na razie nie ma właściwego rozwiązania, jak mówi @spro.

Jeśli jednak masz odpowiedź w locie i używasz ReadableStream, możesz zamknąć strumień, aby anulować żądanie.

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

Let's polyfill:

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;
}

Pamiętaj, że kod nie jest testowany! Daj mi znać, jeśli go przetestowałeś i coś nie działało. Może wyświetlać ostrzeżenia, że ​​próbujesz zastąpić funkcję „pobierania” z oficjalnej biblioteki JavaScript.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.