0%

javascript-cache-request

最近项目中出现一个问题,后端监测系统发现,同样一个请求在短时间内一次性发起了四次,这是极大的浪费,于是考虑使用缓存策略来解决。

首先模拟一个获取后端数据的方法,这里使用setTimeout模拟一个异步请求。这个方法在延时一秒钟后返回一个结构化数据。{id: 1, name: 'Data 1'}

1
2
3
4
5
6
7
8
9
10
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id: 1,
name: 'Data 1',
});
}, 1000);
});
}

接下来我们模拟短时间内的四次请求,可以使用for循环来依次发送四次请求。

1
2
3
4
5
for (let i = 0; i < 4; i++) {
fetchData().then(data => {
console.log(data);
});
}

我们使用async/await语法封装一下上面的for循环。

1
2
3
4
5
6
async function fetchDataFourTimes() {
for (let i = 0; i < 4; i++) {
const data = await fetchData();
console.log(data);
}
}

最后,我们调用一下fetchDataFourTimes方法,查看控制台输出。

1
fetchDataFourTimes();

控制台输出如下:

1
2
3
4
{ id: 1, name: 'Data 1' }
{ id: 1, name: 'Data 1' }
{ id: 1, name: 'Data 1' }
{ id: 1, name: 'Data 1' }

注意,这里有一个问题,我们很难区分这四次请求是不是真的发送了四次,因为返回的数据都是一样的。为了验证后续的cache策略确实能工作,我们可以在fetchData方法中加入一个console.log来验证。

1
2
3
4
5
6
7
8
9
10
11
function fetchData() {
console.log('fetchData');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id: 1,
name: 'Data 1',
});
}, 1000);
});
}

再次调用fetchDataFourTimes方法,查看控制台输出。

1
2
3
4
5
6
7
8
fetchData
{ id: 1, name: 'Data 1' }
fetchData
{ id: 1, name: 'Data 1' }
fetchData
{ id: 1, name: 'Data 1' }
fetchData
{ id: 1, name: 'Data 1' }

接下来,使用缓存策略来解决这个问题。

我们可以改造一下fetchDataFourTimes,使用一个全局变量来存储请求的数据,然后在下次请求时,先判断是否有缓存数据,如果有则直接返回缓存数据,否则发送请求。

1
2
3
4
5
6
7
8
9
10
11
12
const cacheRequest = {};

async function fetchDataFourTimesWithCache() {
for (let i = 0; i < 4; i++) {
if (!cacheRequest['fetchData']) {
cacheRequest['fetchData'] = await fetchData();
} else {
console.log('Hit cache');
console.log(cacheRequest['fetchData']);
}
}
}

然后运行fetchDataFourTimesWithCache方法,查看控制台输出。

1
2
3
4
5
6
7
8
fetchData
{ id: 1, name: 'Data 1' }
Hit cache
{ id: 1, name: 'Data 1' }
Hit cache
{ id: 1, name: 'Data 1' }
Hit cache
{ id: 1, name: 'Data 1' }

可以看到,第一次请求了数据,后面三次都是直接从缓存中获取的,这样就避免了多次请求的问题。

这里有一点需要注意,那就是缓存用的cacheRequest必须是位于缓存函数外部,如果直接放在缓存函数内部,那么每次调用缓存函数都会重新初始化cacheRequest并将其设置为{},导致缓存失效。

我们也可以使用闭包的特性,将cacheRequest封装到函数内部,这样的话,cacheRequest就成了fetchDataFourTimesWithCache函数的内部变量,可以避免全局变量的污染。在fetchDataFourTimesWithCache中,我们返回了一个匿名异步函数,而cacheRequest相对于这个匿名函数是一个外部变量,这样就形成了一个闭包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function fetchDataFourTimesWithCache() {
const cacheRequest = {};

return async () => {
for (let i = 0; i < 4; i++) {
if (!cacheRequest['fetchData']) {
cacheRequest['fetchData'] = await fetchData();
} else {
console.log('Hit cache');
console.log(cacheRequest['fetchData']);
}
}
};
}

const fetchDataFourTimesWithCacheFn = fetchDataFourTimesWithCache();
fetchDataFourTimesWithCacheFn();