要让 async/await 函数支持缓存结果,核心是缓存 promise 实例而非最终值,用 map 存储“参数→promise”映射,首次执行并缓存,后续相同参数直接返回该 promise;需序列化参数作键、缓存错误、封装为装饰器并支持 ttl 与失效清理。

要让 async/await 函数支持缓存结果,核心思路是:**不每次都执行原始异步操作,而是对相同输入返回已缓存的 Promise(或其结果)**。关键在于缓存的是 Promise 本身,而非最终值,避免重复触发异步逻辑,同时保证并发请求共享同一 Promise。
用 Map 缓存 Promise 实例
最常用且可靠的方式是用 Map(或普通对象,但推荐 Map 以支持任意类型键)存储「参数 → 正在进行或已完成的 Promise」的映射。首次调用时执行异步逻辑并存入;后续相同参数直接返回该 Promise。
- 参数需可安全作为键使用——建议将参数序列化为字符串(如
JSON.stringify(args)),或对简单参数(如单个字符串、数字)直接用作键 - 务必缓存 Promise,不是
await后的值,否则每次调用仍会等待,且无法解决并发重复请求问题 - 错误也应被缓存(除非业务要求失败后重试),否则连续调用可能反复报错
示例:
const cache = new Map();
async function fetchUserData(id) {
const key = `user:${id}`;
if (cache.has(key)) {
return cache.get(key);
}
const promise = fetch(`/api/users/${id}`)
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
.catch(err => {
// 可选:清除失败缓存,允许重试
cache.delete(key);
throw err;
});
cache.set(key, promise);
return promise;
}
封装成可复用的缓存装饰器
把缓存逻辑抽离为高阶函数,便于给任意 async 函数添加缓存能力。
- 支持自定义缓存键生成函数(
getKey),适应复杂参数结构 - 支持 TTL(过期时间),通过定时器清理过期项(需额外维护时间戳)
- 注意:若原函数有副作用(如发多次请求),加缓存后行为会改变,需确认是否符合预期
简易装饰器示例:
function memoizeAsync(fn, getKey = (...args) => JSON.stringify(args)) {
const cache = new Map();
return async function(...args) {
const key = getKey(...args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// 使用
const cachedFetchUser = memoizeAsync(
(id) => fetch(`/api/users/${id}`).then(r => r.json()),
(id) => `user:${id}`
);
处理缓存失效与刷新
真实场景中,数据可能更新,需要主动清除缓存。
- 提供
invalidate(key)或clear()方法暴露给外部控制 - 在相关写操作(如
updateUser)后,同步调用cache.delete('user:123') - 对列表类接口(如
getUsers({page: 1})),考虑模糊匹配清除(如删掉所有user:开头的键),或改用更精细的缓存策略
注意事项与边界情况
缓存异步函数看似简单,但容易忽略以下细节:
-
Promise 状态不可逆:一旦 Promise 被 resolve/reject,无法“重置”,所以缓存后想强制刷新必须手动
delete键 - 内存泄漏风险:长期运行的服务中,无限增长的缓存需配合 LRU 策略或 TTL 清理
-
参数引用问题:对象参数直接
JSON.stringify({a:1})可能丢失函数、undefined、Symbol 等,必要时用更健壮的序列化方案(如flatted)或约定只传 plain 对象 - 不适用于依赖实时性的场景:比如获取当前时间、临时 token,缓存反而导致逻辑错误
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











