如何优雅处理JS fetchData函数并发调用重复请求问题?
解决fetchData并发重复请求的优雅方案
核心优化思路
把处于pending状态的请求Promise缓存起来,而非仅缓存最终响应数据。这样并发调用时,后续请求直接复用这个pending的Promise,无需重复发起网络请求。
优化后的代码
function fetchData() { // 已有缓存数据,直接返回已resolve的Promise if (window.data) { return Promise.resolve(window.data); } // 已有正在进行的请求Promise,直接复用 if (window.pendingDataPromise) { return window.pendingDataPromise; } // 发起新请求,先缓存pending的Promise const requestPromise = fetch('http://example.com/data.js') .then(response => response.json()) .then(json => { window.data = json; // 请求完成后清除pending标记(可选,按需调整) delete window.pendingDataPromise; return json; }) .catch(error => { // 请求失败也要清除pending标记,避免后续复用失败的Promise delete window.pendingDataPromise; throw error; }); window.pendingDataPromise = requestPromise; return requestPromise; }
方案说明
- 第一个调用发起请求时,会将请求的Promise存入
window.pendingDataPromise,后续并发调用直接返回这个Promise,等待同一请求完成。 - 请求成功后,将数据存入
window.data,同时删除pendingDataPromise(若业务允许后续清空window.data后重新请求,此步骤必不可少)。 - 增加错误处理:请求失败时清除
pendingDataPromise,防止后续调用一直拿到失败的Promise。
替代方案(Promise不可行时)
若因限制无法使用Promise,可采用自定义事件+回调队列的方式:
function fetchData(callback) { if (window.data) { callback(null, window.data); return; } // 已有请求在进行,将回调加入队列 if (window.dataRequestCallbacks) { window.dataRequestCallbacks.push(callback); return; } // 初始化回调队列 window.dataRequestCallbacks = [callback]; fetch('http://example.com/data.js') .then(res => res.json()) .then(json => { window.data = json; // 通知所有等待的回调 window.dataRequestCallbacks.forEach(cb => cb(null, json)); delete window.dataRequestCallbacks; }) .catch(err => { window.dataRequestCallbacks.forEach(cb => cb(err)); delete window.dataRequestCallbacks; }); }
但该方式需基于回调编写代码,可读性与维护性不如Promise方案,仅作为特殊场景下的备选。
内容的提问来源于stack exchange,提问作者howlettepackage
相关产品推荐
相关产品推荐

