你是否曾经遇到过使用 fetch 请求时无法控制超时时间的尴尬情况?
你是否曾经解决过这个问题,但解决方案却过于局限,缺乏通用性和可重复性?
别担心,今天我就教你一种方法,如何用 AbortController、高阶函数等技术,将原本不具备超时功能的 fetch 进行扩展,实现通用且易复用的超时功能!
阅读本文,跟随子辰一步步解密 fetch 超时功能,通过具体的实现方案和深入的技术分析,带领你解决实际项目中可能遇到的各类问题,让你的技术水平更上一层楼。
实现
要实现基本的超时功能其实很简单,只需要使用 AbortController 这个 API,如果你不熟悉它,可以点击链接了解一下。
我们先来看看简单的代码。
// 首先我们定义一个终止器
const controller = new AbortController();
// 然后调用 fetch 函数,传递一个 url 地址
fetch("https://study.duyiedu.com/api/herolist", {
// 把终止器的信号传递进来
signal: controller.signal,
});
// 设置一个 setTimeout 来测试超时终止,为了方便测试,我们这里写 10ms
setTimeout(() => {
// 终止请求
controller.abort();
}, 10);
可以看到请求已经被取消了。
然后把时间设置成 1000ms 再看看。
可以看到数据在 229 毫秒就返回了,并未到超时时间,即使执行终止请求也是无效的。
但是这个代码呢,虽然能够实现超时功能,却没有任何通用性和可复用性。
如果我们以后还想要使用超时功能,就必须把这段代码重复写一遍。
那么我们就应该把它封装成一个通用的 request 函数。
/**
* @description: 封装一个请求函数
* @param {*} timeout: 传入超时时间
*/
function request(timeout) {
const controller = new AbortController();
fetch("url", {
signal: controller.signal,
});
setTimeout(() => {
controller.abort();
}, timeout);
}
但是这样一来呢,我们就会发现,事情变得更加复杂了。
我们本来只想要处理超时问题,结果却变成了要封装 Ajax。
还要考虑很多其他的问题,比如 url 地址要不要传,fetch 的配置要不要传等等。
所以我们在进行封装的时候就必须考虑,要保持 fetch 的功能不变,不能把 fetch 封装一遍。那样就是小题大做了。
我们希望的是在使用 request 函数的时候,跟使用 fetch 是一样的。
那么这里呢就会让我们想到一个库,叫做 MockJS,做前端开发的同学肯定都知道这个库,它可以拦截 Ajax 请求,并返回模拟数据。
那么它是怎么做到的呢?我们分析一下源码就知道了,其实很简单。
// 拿到原来的 XHR
const oldXHR = XMLHttpRequest;
// 然后给 XHR 重新赋值
window.XMLHttpRequest = function () {
// 在这个新的函数里做一些别的事情
// 有可能还会调用原来的 XHR
new oldXHR();
};
这样做也是一种办法,但是它有侵入性,这样一弄整个系统中所有的 fetch 函数都被改了。
有些 fetch 函数不想使用超时功能或者模拟数据的时候就没办法了。
所以这种方案虽然给了我们一些提示,但是也不是很好。
因为我们要既没有侵入性又有通用性,我们就可以利用高阶函数的方式来处理。
/**
* @description: 创建一个 fetch 函数
* @param {*} timeout: 传入超时的时间
* @return {*} 返回一个新的 fetch 函数
*/
function createFetch(timeout) {
// 返回一个新的 fetch 函数
return (resource, options) => {
// 定义一个终止器
let controller = new AbortController();
// options 配置默认值
options = options || {};
// 向原来的 fetch 配置中加入 signal
options.signal = controller.signal;
setTimeout(() => {
// 当时间到达之后运行 abort
controller.abort();
}, timeout);
return fetch(resource, options);
};
}
// 如果我们不想使用超时功能就可以直接使用 fetch
fetch();
// 如果我们想要使用超时功能就使用 createFetch 创建一个新的 fetch
createFetch(3000)("url", {});
我们还是使用之前的 url 测试一下效果。
createFetch(10)("https://study.duyiedu.com/api/herolist");
createFetch(1000)("https://study.duyiedu.com/api/herolist");
可以看到都是没问题。
而且我们可以看到,超时之后是一个拒绝的 Promise,依然可以做后续的处理。
总结
通过本文,我们学习了如何给 fetch 添加超时功能,并且保持了 fetch 的原有功能和通用性,同时我们还分析了 MockJS 的源码。
对源码的分析不仅让同学们对这个技术本身有了新的认识,还可以为将来的开发提供很多启发和灵感。
所以经常看优质的代码才能提高自己代码质量。