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.