fetch 方法返回的是一个 Promise 对象,而 Promise 里是没有“终止(aborting)”概念的,那么是否表示我们就不能终止 fetch 请求了呢?不是的。
Javascript 提供了一个用于此目的的特殊对象:AbortController,可以用来终止包括 fetch 方法在内的其他异步任务。
AbortController 的使用很简单:
第一步:创建控制器
let controller = new AbortController();
控制器是一个非常简单的对象。
- 提供了一个 abort 方法,还有一个 signal 属性
- 当 abort 方法被调用时
- 会触发 controller.signal 对象上的 abort 事件
- controller.signal.aborted 属性值变为 true
下面举了一个样例(还没用 fetch):
let controller = new AbortController();
let signal = controller.signal;
// triggers when controller.abort() is called
signal.addEventListener('abort', () => alert("abort!"));
controller.abort(); // abort!
alert(signal.aborted); // true
第二步:将 signal 属性传递给 fetch 选项
let controller = new AbortController();
fetch(url, {
signal: controller.signal
});
fetch 方法知道如何与 AbortController 进行协作,它会监听 signal 的 abort 事件。
第三步:退出的话,调用 controller.abort()
controller.abort();
fetch 感知到 signal 发出退出命令了,就终止了请求。
fetch 请求终止后,会携带一个 AbortError 异常返回 reject Promise, 我们可以在 try…catch 中做处理:
// abort in 1 second
let controller = new AbortController();
setTimeout(() => controller.abort(), 1000);
try {
let response = await fetch('/article/fetch-abort/demo/hang', {
signal: controller.signal
});
} catch(err) {
if (err.name == 'AbortError') { // handle abort()
alert("Aborted!");
} else {
throw err;
}
}
AbortController 是可扩展的,我们能使用它同时取消多个 fetch 请求。
下例,我们并行发起了多个 fetch 请求,调用控制器 abort 方法后,所有请求都会被终止:
let urls = [...]; // a list of urls to fetch in parallel
let controller = new AbortController();
let fetchJobs = urls.map(url => fetch(url, {
signal: controller.signal
}));
let results = await Promise.all(fetchJobs);
// if controller.abort() is called from elsewhere,
// it aborts all fetches
当然,如果我们还有除 fetch 之外的其他异步任务,需要一起终止,那么也是可以的。我们只要让这些异步任务使用 controller.signal 监听它的 abort 事件就可以了:
let urls = [...];
let controller = new AbortController();
let ourJob = new Promise((resolve, reject) => { // our task
...
+ controller.signal.addEventListener('abort', reject);
});
let fetchJobs = urls.map(url => fetch(url, { // fetches
signal: controller.signal
}));
// Wait for fetches and our task in parallel
let results = await Promise.all([...fetchJobs, ourJob]);
// if controller.abort() is called from elsewhere,
// it aborts all fetches and ourJob
因此,AbortController 不是仅能用在 fetch 方法上的,它被设计成一个通用对象,用于终止异步任务,只是 fetch 方法天然与它作了集成而已。
(完)