[译] 使用 AbortController 终止 fetch 请求

5,903 阅读3分钟

原文链接:Aborting a fetch request,by GANAPATI V S

在现在的浏览器中,有两种主要的方法发送请求:XMLHttpRequestfetchXMLHttpRequest 这个接口在浏览器中存在很长一段时间了,fetch 则是 ES2015 引入的特性。

XMLHttpRequest 可以在请求中途终止(abortable)。举个例子:

let xhr = new XMLHttpRequest();
xhr.method = 'GET';
xhr.url = 'https://slowmo.glitch.me/5000';
xhr.open(method, url, true);
xhr.send();

// Abort the request at a later stage
abortButton.addEventListener('click', function() {
  xhr.abort();
});

fetch 刚开始引入时并不支持终止请求。Github 上最早 在 2015 年就有终止 fetch 请求的提案 issue 出现。在 fetch 规范之外也有许多解决这个问题的方案,像 cancelable-promises 和其他 hacks

终于,通用的 AbortController 和 AbortSignal API 出来了。该 API 在 DOM 标准 中定义,而不是在语言规范中定义的。

什么是 AbortController

DevTools 中输出的 AbortController 函数

DOM 文档 中有这么一段话:

虽然 Promise 没有提供内置的终止算法(aborting mechanism),但是许多使用它们的 API 需要终止语义。AbortController 提供一个 abort() 方法来支持这些需求,这个方法用来切换相应 AbortSignal 对象的状态。希望支持终止功能的 API 可以接受 AbortSignal 对象,并基于其状态来确定执行流程。

// 创建一个 AbortController 实例 
const controller = new AbortController();
const signal = controller.signal;

// 监听 abort signal(终止信号),调用 controller.abort() 后,会触发这里的回调
signal.addEventListener('abort', () => {
  console.log(signal.aborted); // true
});

// 稍后执行终止操作,这会通知给 signal 知道
controller.abort();

如何使用 AbortController 终止 fetch 请求?

fetch 方法的第二个参数支持配置一个 signal  选项,其值就是 AbortSignal 对象。

const controller = new AbortController();
const signal = controller.signal;

// API 3s 后返回响应
// 注意,这里第二个参数用了一个 'signal' 选项
fetch('https://slowmo.glitch.me/3000', { signal })
  .then(r => r.json())
  .then(response => console.log(response))
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Fetch was aborted');
    } else {
      console.error('Oops!', err);
    }
  });


// 2s 后执行终止操作
// 这会导致 fetch 请求被终止,同时产生一个 'AbortError' 错误。
setTimeout(() => {
  controller.abort();
}, 2000);

这种终止方式,包括终止 fetch 的请求和相应。请求失败时,出现错误 new DOMException('Aborted', 'AbortError')

另外,同一个 AbortSignal(即上面使用的 signal)对象可以用于多个 fetch 请求。

demo

点击这里的链接查看线上 demo:codesandbox.io/s/abortable…

image.png

demo 截图

AbortController 不是说只能给 fetch 使用。它是用来终止异步任务的通用 API。例如,你可以用它来 实现一个可被取消的 Promise

浏览器兼容性和 polyfill

很多旧浏览器还不支持 AbortController  和 AbortSignal  API。下面有可供你使用的 polyfills。

感谢阅读,希望这篇文章能对你有所帮助!

引用

致谢

(正文完)


广告时间(长期有效)

我有一位好朋友开了一间猫舍,在此帮她宣传一下。现在猫舍里养的都是布偶猫。如果你也是个爱猫人士并且有需要的话,不妨扫一扫她的【闲鱼】二维码。不买也不要紧,看看也行。

(完)