await fetch未等待致模板重复加载,缓存机制失效问题
解决模板缓存重复请求问题
问题根源
短时间多次调用loadFile时,第一个请求还未完成(未将内容存入缓存),后续调用检查缓存找不到对应URL,就会重复发起fetch请求。此外代码还存在两个明显问题:
- 全局变量
content会在并发调用时被覆盖,导致结果混乱 catch的错误处理写法错误,无法正确捕获异常
解决方案
核心思路是缓存正在进行的请求Promise,而非仅缓存已加载完成的内容。第一次发起请求时,将请求的Promise存入缓存,后续调用直接等待该Promise完成,避免重复请求。
修改步骤
- 用
Map替代数组作为缓存容器,查找效率更高、结构更清晰 - 缓存项存储两种状态:已加载完成的内容,或是正在进行的请求Promise
- 移除全局
content变量,让loadFile返回对应内容的Promise,确保调用者能正确获取结果 - 修正错误处理逻辑,请求失败时清理缓存,避免后续调用一直等待失败请求
- 保证同一URL的请求仅发起一次,后续调用复用已有请求或缓存内容
完整修改代码
// 用Map存储缓存,key为模板URL,value为已加载内容或正在请求的Promise const cache = new Map(); this.loadFile = async function(url) { // 检查缓存是否存在 if (cache.has(url)) { const cachedItem = cache.get(url); // 如果是Promise,说明请求正在进行,直接等待完成 if (cachedItem instanceof Promise) { console.log("Waiting for ongoing request..."); await cachedItem; } else { console.log("Cached!"); } // 返回缓存的内容 return cache.get(url); } // 无缓存,发起请求 console.log("Fetching..."); // 创建请求Promise并先存入缓存 const requestPromise = fetch(url) .then(response => { if (!response.ok) { throw new Error(`请求失败,状态码:${response.status}`); } return response.text(); }) .then(content => { // 请求完成后,将缓存中的Promise替换为实际内容 cache.set(url, content); console.log("content"); return content; }) .catch(error => { // 请求失败,移除缓存中的失败Promise,允许后续重新请求 cache.delete(url); console.error("加载失败:", error); throw error; // 抛出错误让调用者处理 }); cache.set(url, requestPromise); // 等待请求完成并返回结果 return await requestPromise; }; this.render = function(url) { // 从缓存中获取对应URL的内容(可根据需求调整逻辑) return cache.get(url) || ''; };
调用方式
依然使用await调用,但现在能保证同一URL仅发起一次请求:
await Tpl.loadFile(chrome.runtime.getURL("view/widget.html")); let content = Tpl.render(chrome.runtime.getURL("view/widget.html"));
内容的提问来源于stack exchange,提问作者FMaz008
相关产品推荐
相关产品推荐

