11

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

 3 years ago
source link: https://mp.weixin.qq.com/s/r9ATUsKUJHDdzOSpYtzngA
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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 请求的方法,不知道有没有疑问,欢迎留言!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK