ฉันจะยกเลิกการร้องขอ HTTP fetch () ได้อย่างไร


คำตอบ:


283

TL / DR:

fetchขณะนี้รองรับsignalพารามิเตอร์ ณ วันที่ 20 กันยายน 2560 แต่เบราว์เซอร์บางรุ่นอาจไม่รองรับสิ่งนี้ในขณะนี้

2020 UPDATE:เบราว์เซอร์ส่วนใหญ่ที่สำคัญ (ขอบ, Firefox, Chrome, Safari, Opera, และอื่น ๆ ไม่กี่) สนับสนุนคุณลักษณะซึ่งได้กลายเป็นส่วนหนึ่งของมาตรฐานความเป็นอยู่ DOM (ณ วันที่ 5 มีนาคม 2020)

นี่คือการเปลี่ยนแปลงที่เราจะได้เห็นเร็ว ๆ นี้แม้ว่าและดังนั้นคุณควรจะสามารถที่จะยกเลิกการขอโดยใช้sAbortControllerAbortSignal

รุ่นยาว

ทำอย่างไร:

วิธีการทำงานคือ:

ขั้นตอนที่ 1 : คุณสร้างAbortController(สำหรับตอนนี้ฉันเพิ่งใช้มัน )

const controller = new AbortController()

ขั้นตอนที่ 2 : คุณได้รับAbortControllerสัญญาณแบบนี้:

const signal = controller.signal

ขั้นตอนที่ 3 : คุณผ่านการsignalดึงข้อมูลเช่น:

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

ขั้นตอนที่ 4 : เพียงยกเลิกเมื่อใดก็ตามที่คุณต้องการ:

controller.abort();

นี่คือตัวอย่างของวิธีการใช้งาน (ทำงานบน 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>

แหล่งที่มา:

  • AbortControllerเวอร์ชั่นสุดท้ายได้รับการเพิ่มเข้ากับข้อกำหนดของ DOM
  • ประชาสัมพันธ์ที่สอดคล้องกันสำหรับสเปคสามารถดึงข้อมูลอยู่ในขณะนี้รวม
  • เบราว์เซอร์ข้อบกพร่องการติดตามการดำเนินงานของ AbortController สามารถใช้ได้ที่นี่: Firefox: # 1378342 , โครเมี่ยม: # 750599 , WebKit: # 174980 , ขอบ: # 13009916

2
คำตอบนี้ถูกต้องและควร upvoted แต่ฉันใช้เสรีภาพในการแก้ไขโค้ดบางส่วนเนื่องจากเป็นเพราะมันไม่ได้ทำงานจริงใน Firefox 57+ - shim ดูเหมือนจะทำให้มันล้มเหลว ( “ Err: TypeError: 'signal' สมาชิกของ RequestInit ไม่ใช้อินเทอร์เฟซ AbortSignal” ) และดูเหมือนว่าจะมีปัญหากับใบรับรองสำหรับslowwly.robertomurray.co.uk ( “ เซิร์ฟเวอร์นี้ไม่สามารถพิสูจน์ได้ว่าเป็น slowwly.robertomurray.co.uk) ใบรับรองความปลอดภัยมาจาก * .herokuapp.com.” ) ดังนั้นฉันจึงเปลี่ยนเป็นใช้slowwly.robertomurray.co.uk (http ธรรมดา)
sideshowbarker

3
แต่ตอนนี้มันไม่ทำงานบนเบราว์เซอร์อื่น ๆ เช่น Chrome AbortController is not definedเพราะ อย่างไรก็ตามนี่เป็นเพียงการพิสูจน์แนวคิดอย่างน้อยคนที่มี Firefox 57+ สามารถเห็นมันใช้งานได้
SudoPlz

3
นี่คือทองคำบริสุทธิ์ StackOverflow ขอบคุณสำหรับการเขียนสั้นกระชับ! และตัวเชื่อมโยง bugtracker เช่นกัน!
Kjellski

3
ขณะนี้เบราว์เซอร์ที่ทันสมัยรองรับทั้งหมด developer.mozilla.org/en-US/docs/Web/API/AbortController/abortดูตารางด้านล่าง
Alex Ivasyuv

2
ขอบคุณ แต่ฉันยังมีคำถามเราควรเปลี่ยนสัญญาณกลับเป็นจริงสำหรับการดึงข้อมูลครั้งต่อไปด้วยตนเองหรือไม่?
akshay

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

ทำงานใน 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) และใหม่กว่า


บนเบราว์เซอร์รุ่นเก่าคุณสามารถใช้โพลีฟิลของ whatwg-fetchและAbortController polyfill ของ Githubได้ คุณสามารถตรวจสอบเบราว์เซอร์รุ่นเก่าและใช้ polyfills ตามเงื่อนไขได้เช่นกัน:

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

หากใช้การดึงข้อมูล polyfill ของ github สิ่งนี้สามารถทำได้โดยทำตามคำแนะนำใน readme ของพวกเขา: github.com/github/fetch#aborting-requests
Fábio Santos

@ FábioSantosความคิดเห็นของคุณควรจะเป็นคำถามหรือเป็นคำตอบในสิทธิของตัวเอง? มันไม่ได้ดูเฉพาะคำตอบของฉัน
Jayen

เพียงบันทึกสำหรับผู้ที่ใช้ gitub เรียกโพลีฟิล ฉันคิดว่ามันเกี่ยวข้องกับคำตอบของคุณเพราะ AFAIK เป็นโพลีฟิลสำหรับดึงข้อมูลที่ได้รับความนิยมมากที่สุดและโพลีเติมฟังก์ชันที่คุณใช้เรียกใช้ ผู้คนจำนวนมากจะใช้ polyfill นี้เนื่องจากเบราว์เซอร์เก่า ฉันพบว่าเป็นเรื่องสำคัญที่ต้องพูดถึงเพราะคนคิดว่าพอลิฟิลส์แก้ไขทุกอย่าง แต่อันนี้ไม่พยายามโพลีฟิล AbortController พวกเขาต้องการใช้ AbortController โดยคิดว่ามันจะเป็นโพลีฟิลล์ในเบราว์เซอร์รุ่นเก่าและยุคเฟื่องฟูมีข้อยกเว้นในกรณีมุมและเบราว์เซอร์เก่าเท่านั้น
Fábio Santos

5

ตั้งแต่กุมภาพันธ์ 2018 fetch()สามารถยกเลิกได้ด้วยรหัสด้านล่างบน Chrome (อ่านโดยใช้ Readable Streamsเพื่อเปิดใช้งานการสนับสนุน Firefox) ไม่มีข้อผิดพลาดเกิดcatch()ขึ้นในการรับและนี่เป็นวิธีการแก้ปัญหาชั่วคราวจนกว่าAbortControllerจะได้รับการยอมรับอย่างเต็มที่

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
นี่ไม่ใช่สิ่งที่ OP ขอมา พวกเขาต้องการยกเลิกการดึงข้อมูลไม่ใช่ตัวอ่าน คำสัญญาของการดึงข้อมูลไม่ได้รับการแก้ไขจนกว่าหลังจากคำขอเสร็จสิ้นซึ่งช้าเกินไปที่จะยกเลิกการร้องขอไปยังเซิร์ฟเวอร์
Rahly

3

สำหรับตอนนี้ไม่มีวิธีแก้ไขที่เหมาะสมตามที่ @spro พูด

อย่างไรก็ตามหากคุณมีการตอบสนองบนเที่ยวบินและใช้ ReadableStream คุณสามารถปิดสตรีมเพื่อยกเลิกการร้องขอ

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

โพลีฟิลส์:

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

โปรดทราบว่ารหัสไม่ได้ทดสอบ! แจ้งให้เราทราบหากคุณทำการทดสอบและมีบางอย่างไม่ทำงาน อาจให้คำเตือนว่าคุณพยายามเขียนทับฟังก์ชั่น 'ดึงข้อมูล' จากไลบรารีอย่างเป็นทางการของ JavaScript

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.