
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 calledsignal.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 secondlet 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 parallellet 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, { // fetchessignal: controller.signal}));// Wait for fetches and our task in parallellet results = await Promise.all([...fetchJobs, ourJob]);// if controller.abort() is called from elsewhere,// it aborts all fetches and ourJob
因此,AbortController 不是仅能用在 fetch 方法上的,它被设计成一个通用对象,用于终止异步任务,只是 fetch 方法天然与它作了集成而已。
(完)
