web-dev-qa-db-fra.com

Comment puis-je annuler une requête HTTP fetch ()?

Il existe une nouvelle API pour effectuer des requêtes à partir de JavaScript: fetch (). Existe-t-il un mécanisme intégré pour annuler ces demandes en vol?

154
Sam Lee

TL/DR:

fetch prend désormais en charge un paramètre signal à partir du 20 septembre 2017, mais tous les navigateurs ne semblent pas supporter cette atm .

C’est un changement que nous verrons très bientôt, et vous devriez donc pouvoir annuler une demande en utilisant un AbortControllers AbortSignal.

Version longue

Comment:

Voici comment cela fonctionne:

Étape 1 : vous créez une AbortController (pour l'instant je viens d'utiliser this )

const controller = new AbortController()

Étape 2 : Vous obtenez le signal AbortControllers comme ceci:

const signal = controller.signal

Étape 3 : Vous passez le signal à récupérer comme suit:

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

Étape 4 : Abandonnez à chaque fois que vous devez:

controller.abort();

Voici un exemple de fonctionnement (fonctionne sur 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>

Sources:

  • La version finale de AbortController a été ajoutée à la spécification DOM
  • Le PR correspondant pour la spécification d'extraction est maintenant fusionné.
  • Des bugs de navigateur permettant de suivre l'implémentation de AbortController sont disponibles ici: Firefox: # 1378342 , Chromium: # 750599 , WebKit: # 17498 , Edge: - # 13009916 .
201
SudoPlz

Je ne crois pas qu'il soit possible d'annuler une demande avec l'API d'extraction existante. Des discussions sont en cours à ce sujet sur le site https://github.com/whatwg/fetch/issues/27

Mise à jour de mai 2017: toujours pas de résolution. Les demandes ne peuvent pas être annulées. Plus de discussions sur https://github.com/whatwg/fetch/issues/447

58
spro

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

fonctionne dans Edge 16 (2017-10-17), firefox 57 (2017-11-14), desktop safari 11.1 (2018-03-29), ios safari 11.4 (2018-03-29), chrome 67 (2018-05-29) et plus tard.


sur les anciens navigateurs, vous pouvez utiliser github's whatwg-fetch polyfill et AbortController polyfill . vous pouvez détecter les anciens navigateurs et utiliser les polyfills de manière conditionnelle , aussi:

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
13
Jayen

À compter de février 2018, fetch() peut être annulé à l'aide du code ci-dessous sur Chrome (read tilisation de flux lisibles pour activer le support Firefox). Aucune erreur n'est générée pour que catch() récupère, et il s'agit d'une solution temporaire jusqu'à ce que AbortController soit complètement adopté.

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();
        })
      }
    }
  }
}
6
AnthumChris

Pour l’instant, il n’existe pas de solution adéquate, comme le dit @spro.

Toutefois, si vous avez une réponse en vol et utilisez ReadableStream, vous pouvez fermer le flux pour annuler la demande.

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();
});
3
Brad