¿Cómo cancelo una solicitud HTTP fetch()?

Resuelto Sam Lee asked hace 9 años • 7 respuestas

Hay una nueva API para realizar solicitudes desde JavaScript: fetch(). ¿Existe algún mecanismo integrado para cancelar estas solicitudes durante el vuelo?

Sam Lee avatar Jun 26 '15 05:06 Sam Lee
Aceptado

TL/DR:

fetchahora admite un signalparámetro a partir del 20 de septiembre de 2017, pero no todos los navegadores parecen admitirlo en este momento .

ACTUALIZACIÓN 2020: la mayoría de los principales navegadores (Edge, Firefox, Chrome, Safari, Opera y algunos otros) admiten la función , que se ha convertido en parte del estándar de vida DOM . (a 5 de marzo de 2020)

Sin embargo, este es un cambio que veremos muy pronto, por lo que debería poder cancelar una solicitud utilizando un AbortControllers AbortSignal.

Versión larga

Cómo:

La forma en que funciona es esta:

Paso 1 : creas un AbortController(por ahora solo usé esto )

const controller = new AbortController()

Paso 2 : obtienes la AbortControllerseñal s como esta:

const signal = controller.signal

Paso 3 : Pasas el signalcomando para buscar así:

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

Paso 4 : Simplemente cancele cuando lo necesite:

controller.abort();

Aquí hay un ejemplo de cómo funcionaría (funciona en 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>
Expandir fragmento

Fuentes:

  • La versión final de AbortController se ha agregado a la especificación DOM
  • El PR correspondiente a la especificación de recuperación ahora está fusionado.
  • Los errores del navegador que rastrean la implementación de AbortController están disponibles aquí: Firefox: #1378342 , Chromium: #750599 , WebKit: #174980 , Edge: #13009916 .
SudoPlz avatar Nov 12 '2017 15:11 SudoPlz

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

funciona en 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), y posteriores.


en navegadores más antiguos, puede utilizar whatwg-fetch polyfill y AbortController polyfill de github . También puedes detectar navegadores más antiguos y usar los polyfills de forma condicional :

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
Jayen avatar Oct 17 '2017 08:10 Jayen

Por ahora no existe una solución adecuada, como dice @spro.

Sin embargo, si tiene una respuesta en curso y está utilizando ReadableStream, puede cerrar la transmisión para cancelar la solicitud.

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();
});
Brad avatar Sep 12 '2017 18:09 Brad

A partir de febrero de 2018, fetch()se puede cancelar con el siguiente código en Chrome (lea Usar secuencias legibles para habilitar la compatibilidad con Firefox). No se genera ningún error para catch()corregirlo y esta es una solución temporal hasta que AbortControllerse adopte por completo.

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();
        })
      }
    }
  }
}
anthumchris avatar Feb 22 '2018 16:02 anthumchris

Polillenemos:

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

¡Tenga en cuenta que el código no está probado! Déjame saber si lo has probado y algo no funcionó. Es posible que le advierta que intenta sobrescribir la función 'buscar' de la biblioteca oficial de JavaScript.

0xC0DEGURU avatar May 08 '2020 13:05 0xC0DEGURU