Bagaimana cara membatalkan permintaan HTTP fetch ()?

200

Ada API baru untuk membuat permintaan dari JavaScript: fetch (). Apakah ada mekanisme bawaan untuk membatalkan permintaan ini dalam penerbangan?

Sam Lee
sumber
dapat merujuk davidwalsh.name/cancel-fetch juga
ganesh phirke

Jawaban:

281

TL / DR:

fetchsekarang mendukung signalparameter pada 20 September 2017, tetapi tidak semua browser mendukung hal ini saat ini .

UPDATE 2020: Sebagian besar browser utama (Edge, Firefox, Chrome, Safari, Opera, dan beberapa lainnya) mendukung fitur ini , yang telah menjadi bagian dari standar hidup DOM . (per 5 Maret 2020)

Ini adalah perubahan yang akan segera kami temui, jadi Anda harus dapat membatalkan permintaan dengan menggunakan AbortControllers AbortSignal.

Versi Panjang

Bagaimana cara:

Cara kerjanya adalah ini:

Langkah 1 : Anda membuat AbortController(Untuk saat ini saya baru saja menggunakan ini )

const controller = new AbortController()

Langkah 2 : Anda mendapatkan AbortControllersinyal seperti ini:

const signal = controller.signal

Langkah 3 : Anda lulus signaluntuk mengambil seperti:

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

Langkah 4 : Batalkan saja kapan pun Anda perlu:

controller.abort();

Berikut ini contoh cara kerjanya (berfungsi di 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>

Sumber:

SudoPlz
sumber
2
Jawaban ini benar dan harus ditingkatkan. Tapi saya mengambil kebebasan untuk mengedit cuplikan kode, karena apa adanya sebenarnya tidak berfungsi di Firefox 57+ - shim tampaknya menyebabkannya gagal ( “Err: TypeError: 'signal' anggota RequestInit tidak mengimplementasikan antarmuka AbortSignal. " ) dan tampaknya ada beberapa masalah dengan sertifikat untuk slowwly.robertomurray.co.uk ( " Server ini tidak dapat membuktikan bahwa itu adalah slowwly.robertomurray.co.uk; sertifikat keamanannya berasal dari * .herokuapp.com. " ), jadi saya mengubahnya hanya menggunakan slowwly.robertomurray.co.uk (http polos).
sontonbarker
3
Tapi sekarang tidak berfungsi di browser lain yaitu Chrome karena AbortController is not defined. Pokoknya ini hanya bukti konsep, setidaknya orang-orang dengan Firefox 57+ dapat melihatnya berfungsi
SudoPlz
3
Ini adalah emas StackOverflow murni, terima kasih atas penulisan singkatnya! Dan tautan bugtracker juga!
Kjellski
3
Sekarang semua browser modern mendukungnya. developer.mozilla.org/en-US/docs/Web/API/AbortController/abort lihat tabel di bagian bawah
Alex Ivasyuv
2
Terima kasih tapi saya masih punya pertanyaan, haruskah kita mengubah sinyal kembali ke true untuk pengambilan berikutnya secara manual ??
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();

bekerja di 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) -29), dan nanti.


pada browser lama, Anda dapat menggunakan polyfill whatwg-fetch github dan AbortController polyfill . Anda dapat mendeteksi browser yang lebih lama dan menggunakan polyfill secara kondisional juga:

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
sumber
Jika menggunakan polyfill fetch github, ini mungkin dilakukan, cukup ikuti instruksi pada readme mereka: github.com/github/fetch#aborting-requests
Fábio Santos
@ FábioSantos Haruskah komentar Anda ada pada pertanyaan, atau sebagai jawaban di dalamnya sendiri? Itu tidak terlihat spesifik untuk jawaban saya.
Jayen
Hanya sebuah catatan untuk orang-orang yang menggunakan github mengambil polyfill. Saya pikir itu relevan dengan jawaban Anda karena AFAIK itu adalah pengambilan polyfill paling populer yang tersedia, dan itu polyfill fungsi yang Anda gunakan, ambil. Banyak orang akan menggunakan polyfill ini karena browser lama. Saya merasa penting untuk menyebutkan karena orang hanya menganggap polyfill memperbaiki semuanya, tetapi yang satu ini tidak mencoba untuk polyfill AbortController. Mereka akan mencoba menggunakan AbortController berpikir itu akan di-polyfill di browser lama, dan boom, ada pengecualian di kasing sudut dan hanya di browser lama.
Fábio Santos
5

Mulai Februari 2018, fetch()dapat dibatalkan dengan kode di bawah ini di Chrome (baca Menggunakan Aliran yang Dapat Dibaca untuk mengaktifkan dukungan Firefox). Tidak ada kesalahan yang dilemparkan untuk catch()mengambil, dan ini adalah solusi sementara sampai AbortControllersepenuhnya diadopsi.

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
sumber
2
Ini BUKAN apa yang diminta OP. Mereka ingin membatalkan pengambilan bukan pembaca. Janji pengambilan tidak diselesaikan sampai SETELAH permintaan selesai, yang terlambat untuk membatalkan permintaan ke server.
Rahly
3

Adapun saat ini tidak ada solusi yang tepat, seperti kata @spro.

Namun, jika Anda memiliki respons dalam penerbangan dan menggunakan ReadableStream, Anda dapat menutup streaming untuk membatalkan permintaan.

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
sumber
0

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

Perlu diingat bahwa kode ini tidak diuji! Beri tahu saya jika Anda telah mengujinya dan ada yang tidak berhasil. Mungkin memberi Anda peringatan bahwa Anda mencoba untuk menimpa fungsi 'fetch' dari perpustakaan resmi JavaScript.

0xC0DEGURU
sumber