Làm cách nào để tôi hủy yêu cầu tìm nạp HTTP ()?


Câu trả lời:


281

TL / DR:

fetchhiện hỗ trợ một signaltham số kể từ ngày 20 tháng 9 năm 2017, nhưng hiện tại không phải tất cả các trình duyệt đều hỗ trợ điều này .

CẬP NHẬT 2020: Hầu hết các trình duyệt chính (Edge, Firefox, Chrome, Safari, Opera và một vài trình duyệt khác) đều hỗ trợ tính năng này , đã trở thành một phần của tiêu chuẩn sống DOM . (kể từ ngày 5 tháng 3 năm 2020)

Đây là một thay đổi mà chúng tôi sẽ sớm thấy, và vì vậy bạn sẽ có thể hủy yêu cầu bằng cách sử dụng AbortControllers AbortSignal.

Phiên bản dài

Làm thế nào để:

Cách thức hoạt động là thế này:

Bước 1 : Bạn tạo một AbortController(Bây giờ tôi chỉ sử dụng cái này )

const controller = new AbortController()

Bước 2 : Bạn nhận được AbortControllertín hiệu s như thế này:

const signal = controller.signal

Bước 3 : Bạn vượt qua signalđể tìm nạp như vậy:

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

Bước 4 : Chỉ cần hủy bỏ bất cứ khi nào bạn cần:

controller.abort();

Đây là một ví dụ về cách nó sẽ hoạt động (hoạt động trên 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>

Nguồn:

  • Phiên bản cuối cùng của AbortControll đã được thêm vào đặc tả DOM
  • Các PR tương ứng cho các đặc điểm kỹ thuật lấy bây giờ được sáp nhập.
  • Lỗi trình duyệt theo dõi việc triển khai AbortControll có sẵn tại đây: Firefox: # 1378342 , Chromium: # 750599 , WebKit: # 174980 , Edge: # 13009916 .

2
Câu trả lời này là chính xác và nên được nâng cao. Nhưng tôi đã tự do thực hiện một số chỉnh sửa cho đoạn mã, bởi vì nó thực sự không hoạt động trong Firefox 57+ - shim dường như khiến nó thất bại ( thành viên Err: TypeError: 'signal' của RequestInit không triển khai giao diện AbortSignal. hung ) và dường như có một số vấn đề với chứng nhận cho Slowwly.robertom Hur.co.uk ( Nhà máy này không thể chứng minh rằng nó là Slowwly.robertom Hur.co.uk; chứng chỉ bảo mật của nó là từ * .herokuapp.com .và ), vì vậy tôi đã thay đổi nó thành chỉ sử dụng Slowwly.robertom bồ.co.uk (http đơn giản).
sIDIAbarker

3
Nhưng bây giờ nó không hoạt động trên các trình duyệt khác, ví dụ như Chrome AbortController is not defined. Dù sao đây chỉ là một bằng chứng về khái niệm, ít nhất những người có Firefox 57+ có thể thấy nó hoạt động
SudoPlz

3
Đây là vàng StackOverflow thuần túy, cảm ơn vì đã viết ngắn gọn! Và các liên kết bugtracker là tốt!
Kjellski

3
Bây giờ tất cả các trình duyệt hiện đại hỗ trợ nó. developer.mozilla.org/en-US/docs/Web/API/AbortControll/abort xem bảng ở phía dưới
Alex Ivasyuv

2
Cảm ơn nhưng tôi vẫn có một câu hỏi, chúng ta có nên thay đổi tín hiệu trở lại đúng cho lần tìm nạp tiếp theo không?
akshay kishore

20

https://developers.google.com/web/updates/2017/09/abortable-fetch

https://dom.spec.whatwg.org/#aborting-ongaging-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();

hoạt động ở cạnh 16 (2017-10-17), firefox 57 (2017-11-14), máy tính để bàn 11.1 (2018-03-29), ios safari 11.4 (2018-03-29), chrome 67 (2018-05 -29) và sau đó.


trên các trình duyệt cũ hơn, bạn có thể sử dụng polyfill whatwg-fetch polyfillAbortControll của github . bạn cũng có thể phát hiện các trình duyệt cũ hơn và sử dụng các polyfill theo điều kiện :

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

Nếu sử dụng polyfill tìm nạp của github, điều này có thể thực hiện được với nó, chỉ cần làm theo hướng dẫn trên readme của họ: github.com/github/fetch#aborting-requests
Fábio Santos

@ FábioSantos Bình luận của bạn nên được đặt câu hỏi, hoặc là một câu trả lời trong chính nó? Nó không có vẻ cụ thể cho câu trả lời của tôi.
Jayen

Chỉ cần một lưu ý cho những người đang sử dụng polyfill lấy github. Tôi nghĩ rằng nó có liên quan đến câu trả lời của bạn bởi vì AFAIK là phần mềm tìm nạp phổ biến nhất hiện có và nó hoàn thành chức năng bạn đang sử dụng, tìm nạp. Rất nhiều người sẽ sử dụng polyfill này vì các trình duyệt cũ. Tôi thấy điều quan trọng cần đề cập bởi vì mọi người chỉ cho rằng polyfill sửa chữa mọi thứ, nhưng điều này đặc biệt không cố gắng để polyfill AbortControll. Họ sẽ cố gắng sử dụng AbortControll với suy nghĩ rằng nó sẽ được đa dạng hóa trong các trình duyệt cũ và bùng nổ, có một ngoại lệ trong trường hợp góc và chỉ trên các trình duyệt cũ.
Fábio Santos

5

Kể từ tháng 2 năm 2018, fetch()có thể bị hủy bằng mã bên dưới trên Chrome (đọc Sử dụng luồng có thể đọc để bật hỗ trợ Firefox). Không có lỗi được đưa ra catch()để nhận, và đây là một giải pháp tạm thời cho đến khi AbortControllerđược chấp nhận hoàn toàn.

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
Đây không phải là những gì OP yêu cầu. Họ muốn hủy bỏ tìm nạp không phải người đọc. Lời hứa của Fetch không giải quyết cho đến khi SAU yêu cầu kết thúc, quá muộn để hủy yêu cầu đến máy chủ.
Rahly

3

Hiện tại không có giải pháp thích hợp, như @spro nói.

Tuy nhiên, nếu bạn có phản hồi trên máy bay và đang sử dụng ReadableStream, bạn có thể đóng luồng để hủy yêu cầu.

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

Hãy để 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;
}

Xin lưu ý rằng mã không được kiểm tra! Hãy cho tôi biết nếu bạn đã thử nghiệm nó và một cái gì đó không hoạt động. Nó có thể đưa ra cảnh báo rằng bạn cố gắng ghi đè chức năng 'tìm nạp' từ thư viện chính thức JavaScript.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.