@wfp99/async-lru-cache
v1.2.0
Published
A simple memory cache using the LRU algorithm and supporting asynchronous data access.
Maintainers
Readme
Async LRU Cache
一個支援非同步載入與保存操作、並自動處理並發請求的 LRU(Least Recently Used)記憶體快取,適用於 Node.js 與 TypeScript。
特色
- 支援非同步的載入器(loader)與保存器(saver)函數
- 對同一 key 的並發
get()呼叫共用單一載入器執行 - 對同一 key 的多個
put()呼叫會序列化並依序執行 - 超出設定容量時自動執行 LRU 淘汰
- 支援每筆項目的可選 TTL(存活時間),並可設定週期性背景清理
- 可透過
onError自訂錯誤處理,預設為記錄到 console get()可省略 loader 進行唯讀查詢;peek()則提供完全不影響 LRU 順序的查詢方式
安裝
npm install @wfp99/async-lru-cache系統需求
- Node.js >= 14.0.0
使用方法
基本使用
import { AsyncLRUCache } from '@wfp99/async-lru-cache';
const cache = new AsyncLRUCache<string, User>({ capacity: 100 });
// get() 在快取未命中時呼叫 loader,並快取其回傳值。
const user = await cache.get('user:123', async () => {
const response = await fetch('/api/users/123');
return response.json();
});
// put() 直接儲存值,並可選擇透過 saver 進行持久化。
await cache.put('user:456', userData, async (key, value) => {
await saveToDatabase(key, value);
});唯讀查詢
// 省略 loader 的 get() 屬於唯讀查詢:命中時行為與一般 get() 相同(會更新 LRU 順序);
// 未命中時回傳 undefined,且不會新增任何項目或影響 LRU 順序。
const cached = await cache.get('user:123'); // User | undefined
// peek() 無論命中或未命中都不會影響 LRU 順序。
const peeked = await cache.peek('user:123'); // Promise<User> | undefinedTTL(存活時間)
const cache = new AsyncLRUCache({
capacity: 100,
defaultTtlMs: 5000, // 套用到每筆項目的預設 TTL
cleanupIntervalMs: 10000, // 週期性背景清理過期項目
});
// 為特定項目覆寫預設 TTL。
await cache.get('key1', async () => fetchData(), 30000);
cache.has('key1'); // 項目過期後回傳 false
cache.cleanupExpired(); // 手動移除過期項目
cache.destroy(); // 停止清理計時器並清除所有資料並發處理
// 以下並發的 get() 呼叫會共用同一個 loader 執行。
const [a, b, c] = await Promise.all([
cache.get('shared-key', loader),
cache.get('shared-key', loader),
cache.get('shared-key', loader),
]);
// 以下 put() 呼叫即使並發啟動,也會依序序列化執行。
cache.put('key', 'value1', saver1);
cache.put('key', 'value2', saver2);
cache.put('key', 'value3', saver3);錯誤處理
- 若載入器(
get)拋出例外,該項目會從快取中移除,錯誤會傳遞給呼叫端。 - 若保存器(
put)拋出例外,該項目也會被移除,避免快取到尚未持久化的值。 put()會忽略同一 key 前一個已結束操作的失敗,讓新的操作可以繼續進行。- 預設情況下,上述失敗會透過
console.error/console.warn記錄。若要自行處理,可提供onError:
const cache = new AsyncLRUCache({
capacity: 100,
onError(error, { key, source }) {
// source 為 'loader' | 'saver' | 'put-chain'
myLogger.error(`cache ${source} failed for ${key}`, error);
},
});API 參考
AsyncLRUCache<K, V>
constructor(option: AsyncLRUCacheOption<K>)AsyncLRUCacheOption<K>
| 屬性 | 類型 | 必需 | 描述 |
|---|---|---|---|
| capacity | number | 是 | 快取中允許的最大項目數量,最小值為 10 |
| defaultTtlMs | number | 否 | 套用到未指定 TTL 之項目的預設 TTL |
| cleanupIntervalMs | number | 否 | 自動背景清理過期項目的執行間隔 |
| onError | (error: unknown, context: { key: K; source: 'loader' \| 'saver' \| 'put-chain' }) => void | 否 | 載入器/保存器失敗時的回呼函數。提供後將取代預設的 console 記錄行為 |
方法
| 方法 | 描述 |
|---|---|
| get(key, loader, ttlMs?) | 回傳快取值;未命中時呼叫 loader() 並快取其結果 |
| get(key) | 省略 loader 的唯讀查詢。命中時行為與上方多載相同(會更新 LRU 順序);未命中時回傳 undefined,不會異動快取 |
| peek(key) | 回傳快取值的 Promise<V>;若不存在或已過期則回傳 undefined。無論命中與否都不影響 LRU 順序 |
| put(key, value, saver?, ttlMs?) | 儲存 value,並可選擇等待 saver(key, value) 進行持久化 |
| invalidate(key) | 若存在,移除 key 對應的項目 |
| clear() | 移除所有項目 |
| has(key) | 回傳 key 是否存在且未過期。此檢查會順帶清除已過期的項目,但不影響 LRU 順序 |
| size() | 回傳目前的項目數量 |
| cleanupExpired() | 立即移除所有過期項目 |
| destroy() | 停止清理計時器(若有)並清除所有資料 |
TypeScript
此套件以 TypeScript 撰寫並內建型別宣告檔,無需另外安裝 @types 套件。
授權
MIT
貢獻
歡迎在 GitHub 上提出問題和發送 Pull Request。
