Javascript 如何取消 HTTP fetch() 请求?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31061838/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 06:09:25  来源:igfitidea点击:

How do I cancel an HTTP fetch() request?

javascriptajaxfetch-api

提问by Sam Lee

There is a new API for making requests from JavaScript: fetch(). Is there any built in mechanism for canceling these requests in-flight?

有一个用于从 JavaScript 发出请求的新 API:fetch()。是否有任何内置机制可以取消这些正在进行的请求?

回答by SudoPlz

TL/DR:

TL/DR:

fetchnow supports a signalparameter as of 20 September 2017, but not all browsers seem support this at the moment.

fetch现在支持signal截至 2017 年 9 月 20 日的参数,但目前似乎并非所有浏览器都支持此参数

2020 UPDATE:Most major browsers (Edge, Firefox, Chrome, Safari, Opera, and a few others) support the feature, which has become part of the DOM living standard. (as of 5 March 2020)

2020 更新:大多数主要浏览器(Edge、Firefox、Chrome、Safari、Opera 和其他一些浏览器)都支持该功能,该功能已成为DOM 生活标准的一部分。(截至 2020 年 3 月 5 日)

This is a change we will be seeing very soon though, and so you should be able to cancel a request by using an AbortControllers AbortSignal.

这是我们很快就会看到的更改,因此您应该能够使用AbortControllers取消请求AbortSignal

Long Version

长版

How to:

如何:

The way it works is this:

它的工作方式是这样的:

Step 1: You create an AbortController(For now I just used this)

第 1 步:您创建一个AbortController(现在我只使用了这个

const controller = new AbortController()

const controller = new AbortController()

Step 2: You get the AbortControllers signal like this:

第 2 步:您会得到这样的AbortControllers 信号:

const signal = controller.signal

const signal = controller.signal

Step 3: You pass the signalto fetch like so:

第 3 步:您通过signalfetch 像这样:

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

Step 4: Just abort whenever you need to:

第 4 步:只需在需要时中止:

controller.abort();

controller.abort();

Here's an example of how it would work (works on Firefox 57+):

这是它如何工作的示例(适用于 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:

资料来源:

回答by Jayen

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

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

https://dom.spec.whatwg.org/#aborting-ongoing-activities

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

works in 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), and later.

适用于 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),以及以后。



on older browsers, you can use github's whatwg-fetch polyfilland AbortController polyfill. you can detect older browsers and use the polyfills conditionally, too:

在较旧的浏览器上,您可以使用github 的 whatwg-fetch polyfillAbortController polyfill。您也可以检测较旧的浏览器并有条件地使用 polyfill

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

回答by AnthumChris

As of Feb 2018, fetch()can be cancelled with the code below on Chrome (read Using Readable Streamsto enable Firefox support). No error is thrown for catch()to pick up, and this is a temporary solution until AbortControlleris fully adopted.

截至 2018 年 2 月,fetch()可以在 Chrome 上使用以下代码取消(阅读使用可读流以启用 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();
        })
      }
    }
  }
}

回答by Brad

As for now there is no proper solution, as @spro says.

至于现在没有合适的解决方案,正如@spro 所说。

However, if you have an in-flight response and are using ReadableStream, you can close the stream to cancel the request.

但是,如果您有一个进行中的响应并且正在使用 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();
});

回答by 0xC0DEGURU

Let's 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;
}

Please have in mind that the code is not tested! Let me know if you have tested it and something didn't work. It may give you warnings that you try to overwrite the 'fetch' function from the JavaScript official library.

请记住,代码未经测试!让我知道您是否已经对其进行了测试并且某些功能不起作用。它可能会警告您尝试覆盖 JavaScript 官方库中的 'fetch' 函数。