JS应用中仅缓存WMTS服务getCapabilities响应的重复请求问题
问题分析与解决方案
核心问题
你碰到的两个关键问题:
- Map缓存被重复初始化:如果承载缓存的模块被多次加载(比如打包配置问题、不同路径重复导入),每个模块实例都会创建新的Map,导致缓存完全失效。
- 并发请求竞态:第一次请求还在处理时,第二次调用检查缓存发现为空,会重复发起相同请求。
修复方案
1. 确保缓存是单例
ES模块默认是单例,但如果项目存在重复导入场景,建议把缓存抽成独立模块,彻底保证单例:
// cache.js export const capabilitiesCache = new Map();
然后在你的函数模块中导入:
import { capabilitiesCache } from './cache.js';
2. 缓存Promise避免并发重复请求
原代码只缓存最终解析结果,并发场景下会重复发起请求。改为缓存请求的Promise,让后续调用直接等待同一个请求完成:
import { capabilitiesCache } from './cache.js'; export async function getCapabilities(layer, capabilitiesTimeout) { const libFetch = require('whatwg-fetch').fetch; const view = layer.views[0]; if (!view) throw new Error('图层无可用视图配置'); const { baseUrl, serviceType, serviceVersion } = view; // 用完整请求参数作为缓存键,避免不同参数的请求共用缓存 const cacheKey = `${baseUrl}?SERVICE=${serviceType}&REQUEST=GetCapabilities&VERSION=${serviceVersion}`; // 缓存存在则直接返回(可能是pending的Promise或已完成的结果) if (capabilitiesCache.has(cacheKey)) { return capabilitiesCache.get(cacheKey); } const url = new URL(baseUrl); url.searchParams.append('SERVICE', serviceType); url.searchParams.append('REQUEST', 'GetCapabilities'); url.searchParams.append('VERSION', serviceVersion); // 创建请求Promise并缓存 const requestPromise = (async () => { try { const response = await timeout(capabilitiesTimeout, fetch(getProxiedUrl(url))); const text = await response.text(); let parsedResponse; if (serviceType === TYPE_WMTS) { parsedResponse = parser.read(text); } else if (serviceType === TYPE_WMS) { parsedResponse = parserWms.read(text); } return parsedResponse; } catch (e) { console.warn( `'GetCapabilities' 超时(${capabilitiesTimeout}ms),图层:"${layer?.title}"`, e ); // 出错时移除缓存,避免后续调用直接获取错误Promise capabilitiesCache.delete(cacheKey); throw e; } })(); capabilitiesCache.set(cacheKey, requestPromise); return requestPromise; }
3. 可选:其他缓存方案
如果需要更复杂的缓存策略,比如过期时间、容量限制,可以考虑:
- LRU缓存:使用
lru-cache库,设置最大缓存数量和过期时间,自动淘汰不常用的缓存。 - 持久化缓存:用
localStorage/sessionStorage存储序列化后的响应,但注意只能存储字符串,需要处理解析/序列化逻辑,适合跨会话缓存场景。
内容的提问来源于stack exchange,提问作者cam_gis
相关产品推荐
相关产品推荐

