如何取消 HTTP fetch() 请求?
How do I cancel an HTTP fetch() request?
有一个新的 API 用于从 JavaScript 发出请求:fetch()
。是否有任何内置机制可以在运行中取消这些请求?
正如@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();
});
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)、桌面 safari 11.1 (2018-03-29)、ios safari 11.4 (2018-03- 29), chrome 67 (2018-05-29), 及以后.
在旧版浏览器上,您也可以使用 github's whatwg-fetch polyfill and AbortController polyfill. you can detect older browsers and use the polyfills conditionally:
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
TL/DR:
自 2017 年 9 月 20 日起,fetch
现在支持 signal
参数,但 不支持
目前所有浏览器似乎都支持此功能。
2020 年更新: 大多数主流浏览器(Edge、Firefox、Chrome、Safari、Opera 和其他一些浏览器)support the feature, which has become part of the DOM living standard。 (截至 2020 年 3 月 5 日)
我们很快就会看到这一变化,因此您应该可以使用 AbortController
s AbortSignal
.
取消请求
长版
如何:
它的工作方式是这样的:
第 1 步:您创建一个 AbortController
(现在我只使用 this)
const controller = new AbortController()
第 2 步:你会得到这样的 AbortController
s 信号:
const signal = controller.signal
第 3 步:您将 signal
传递给 fetch,如下所示:
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 规范中
- 提取规范的 corresponding PR 现已合并。
- 跟踪 AbortController 实现的浏览器错误可在此处找到:Firefox:#1378342, Chromium: #750599, WebKit: #174980, Edge: #13009916。
自 2018 年 2 月起,可以在 Chrome 上使用以下代码取消 fetch()
(阅读 Using 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();
})
}
}
}
}
让我们填充:
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 官方库中的 'fetch' 函数。
这适用于浏览器和 nodejs Live browser demo
const cpFetch= require('cp-fetch');
const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=3s';
const chain = cpFetch(url, {timeout: 10000})
.then(response => response.json())
.then(data => console.log(`Done: `, data), err => console.log(`Error: `, err))
setTimeout(()=> chain.cancel(), 1000); // abort the request after 1000ms
简单的打字稿版本(获取被中止):
export async function fetchWithTimeout(url: RequestInfo, options?: RequestInit, timeout?: number) {
return new Promise<Response>((resolve, reject) => {
const controller = new AbortController();
const signal = controller.signal;
const timeoutId = setTimeout(() => {
console.log('TIMEOUT');
reject('Timeout');
// Cancel fetch in progress
controller.abort();
}, timeout ?? 5 * 1000);
fetch(url, { ...options, signal })
.then((response) => {
clearTimeout(timeoutId);
resolve(response);
})
.catch((e) => reject(e));
});
}
也许您需要一个 polyfill(例如 IE11):
https://polyfill.io/v3/polyfill.min.js?features=AbortController
有一个新的 API 用于从 JavaScript 发出请求:fetch()
。是否有任何内置机制可以在运行中取消这些请求?
正如@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();
});
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)、桌面 safari 11.1 (2018-03-29)、ios safari 11.4 (2018-03- 29), chrome 67 (2018-05-29), 及以后.
在旧版浏览器上,您也可以使用 github's whatwg-fetch polyfill and AbortController polyfill. you can detect older browsers and use the polyfills conditionally:
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
TL/DR:
自 2017 年 9 月 20 日起,fetch
现在支持 signal
参数,但 不支持
目前所有浏览器似乎都支持此功能。
2020 年更新: 大多数主流浏览器(Edge、Firefox、Chrome、Safari、Opera 和其他一些浏览器)support the feature, which has become part of the DOM living standard。 (截至 2020 年 3 月 5 日)
我们很快就会看到这一变化,因此您应该可以使用 AbortController
s AbortSignal
.
长版
如何:
它的工作方式是这样的:
第 1 步:您创建一个 AbortController
(现在我只使用 this)
const controller = new AbortController()
第 2 步:你会得到这样的 AbortController
s 信号:
const signal = controller.signal
第 3 步:您将 signal
传递给 fetch,如下所示:
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 规范中
- 提取规范的 corresponding PR 现已合并。
- 跟踪 AbortController 实现的浏览器错误可在此处找到:Firefox:#1378342, Chromium: #750599, WebKit: #174980, Edge: #13009916。
自 2018 年 2 月起,可以在 Chrome 上使用以下代码取消 fetch()
(阅读 Using 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();
})
}
}
}
}
让我们填充:
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 官方库中的 'fetch' 函数。
这适用于浏览器和 nodejs Live browser demo
const cpFetch= require('cp-fetch');
const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=3s';
const chain = cpFetch(url, {timeout: 10000})
.then(response => response.json())
.then(data => console.log(`Done: `, data), err => console.log(`Error: `, err))
setTimeout(()=> chain.cancel(), 1000); // abort the request after 1000ms
简单的打字稿版本(获取被中止):
export async function fetchWithTimeout(url: RequestInfo, options?: RequestInit, timeout?: number) {
return new Promise<Response>((resolve, reject) => {
const controller = new AbortController();
const signal = controller.signal;
const timeoutId = setTimeout(() => {
console.log('TIMEOUT');
reject('Timeout');
// Cancel fetch in progress
controller.abort();
}, timeout ?? 5 * 1000);
fetch(url, { ...options, signal })
.then((response) => {
clearTimeout(timeoutId);
resolve(response);
})
.catch((e) => reject(e));
});
}
也许您需要一个 polyfill(例如 IE11):
https://polyfill.io/v3/polyfill.min.js?features=AbortController