如何在客户端终止一个已经发出的 HTTP 请求

Javascript 异步编程得益于 Promise 的实现,它们极大地提高了Web开发的性能和体验。不过原生的 Promise 有个最大的缺点就是一旦请求发出去,我们就无法取消它。但是我们找到了另一种方法来实现。
DOM 标准中添加了一个称为 AbortController 的新的控制器,该控制器允许我们将其用作取消HTTP fetch 请求的信号。它利用 AbortSignal 属性来执行此操作。
它是在2017年添加的,并且在大多数浏览器中都支持(显然,除了IE)。但是既然已经抛弃对IE的支持,因此这真的是一件大事。

为什么我们需要取消 HTTP 请求?

在我们讨论如何去取消之前,我们先了解下为什么需要取消 HTTP fetch 请求。当我们的应用程序是由可以动态添加到 DOM 树和从 DOM 树删除的多个组件组成的时候,许多组件都必须独立进行相应的HTTP请求。可能发生的情况是,在获取请求完成之前,将其中一个组件卸载。这种情况多发生在网速慢的情况下,或者在页面正在跳转时用户离开了该页面。
由于请求是异步的,因此它将在后台继续执行。并且如果处理不当,可能会导致一些错误。
谷歌浏览器的fetch请求默认超时时间是300秒,火狐浏览器是90秒。这些还都是在网络正常的情况下。因此,如果需要的话,我们绝对希望通过自己的方式来终止HTTP的fetch请求。

AbortController和AbortSignal

AbortController和AbortSignal API是由DOM标准提供,这样即使Web标准变化了它仍然可以继续使用。我们来声明一个控制器:

const controller = new AbortController();

和一个signal常量

const signal = controller.signal;

这个控制器仅仅只有一个abort方法。并且该方法一旦被触发,立即通知signal

controller.abort();
signal.addEventListener('abort', () => {
console.log(signal.aborted); // true
});

如何取消一个HTTP fetch请求

fetch Api本身不允许以编程方式取消请求。但是它可以将AbortSignal作为参数。这样一来,如果我们一旦需要,我们就可以在特定的时间段内中止这个请求。

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




fetch(url, { signal })
.catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch was aborted');
}
});
// Abort the request after 4s
// aborts the fetch with 'AbortError'
setTimeout(() => {
controller.abort();
}, 4000);

我们甚至可以自己封装一个带有延迟参数的fetch请求的函数

async function cancellableFetch(url, data, timeout = 4000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);


const response = await fetch(url, {
...data,
signal: controller.signal
});
clearTimeout(timer);
return response;
}

值得注意的是:

  • 如果我们将同一个signal传递给多个请求,如果一旦终止就取消了所有的请求。所以,它只能在如果一个请求失败将取消所有请求的情况下使用。
  • 由于控制器不能多次使用,如果我们不想在取消一个HTTP fetch请求时终止所有的请求,那么就需要为所有的控制器创建一个新的实例。
  • 请求的取消仅发生在客户端。尽管客户端收不到响应,服务器仍可能会对其进行处理。
  • 我们也可以使用Promise.race来实现此功能,但是该解决方案会使请求挂起而不是中止请求。它将继续在后台消耗带宽。

以上就是在耗时终止HTTP fetch 请求的方法,不知道有没有疑问,欢迎留言!