最近项目中出现一个问题,后端监测系统发现,同样一个请求在短时间内一次性发起了四次,这是极大的浪费,于是考虑使用缓存策略来解决。
首先模拟一个获取后端数据的方法,这里使用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 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();
|