@wfp99/async-lru-cache 1.1.1 โ†’ 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,29 +4,17 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Node.js Version](https://img.shields.io/badge/node-%3E%3D14.0.0-brightgreen.svg)](https://nodejs.org/)
6
6
 
7
- An asynchronous LRU (Least Recently Used) memory cache that supports asynchronous loading and saving operations while automatically handling concurrent requests.
7
+ An in-memory LRU (Least Recently Used) cache for Node.js and TypeScript, with support for asynchronous loading, saving, and automatic deduplication of concurrent requests.
8
8
 
9
9
  ## Features
10
10
 
11
- - ๐Ÿš€ **Async Support**: Supports asynchronous loader and saver functions
12
- - ๐Ÿ”„ **Automatic Merging**: Automatically merges concurrent GET requests for the same key
13
- - ๐Ÿ“ **Serialized Writes**: Serializes PUT operations for the same key to ensure execution order
14
- - ๐Ÿ—‘๏ธ **LRU Eviction**: Implements LRU algorithm to automatically remove least recently used items
15
- - โฐ **TTL Support**: Automatic expiration of cache entries with configurable Time To Live
16
- - ๐Ÿ›ก๏ธ **Error Handling**: Comprehensive error handling with automatic cache cleanup on failures
17
- - ๐Ÿงน **Complete Clearing**: Support for clearing all cache entries at once
18
-
19
- ## Why Choose In-Memory Over External Cache Services?
20
-
21
- **Simplicity & Performance**
22
- - No setup required - install and use immediately
23
- - Zero network latency - direct memory access
24
- - Reduced infrastructure complexity
25
-
26
- **Reliability & Development**
27
- - No external dependencies or network failures
28
- - Simplified testing and debugging
29
- - Works without Redis, Memcached, or other cache servers
11
+ - Asynchronous loader (`get`) and saver (`put`) functions
12
+ - Concurrent `get()` calls for the same key share a single loader execution
13
+ - `put()` calls for the same key are serialized and execute in order
14
+ - LRU eviction once the configured capacity is exceeded
15
+ - Optional per-entry TTL (time to live), with optional periodic background cleanup
16
+ - Configurable error handling via `onError`, defaulting to console logging
17
+ - `get()` can be called without a loader for a read-only lookup, and `peek()` for a lookup that never affects LRU order
30
18
 
31
19
  ## Installation
32
20
 
@@ -40,241 +28,122 @@ npm install @wfp99/async-lru-cache
40
28
 
41
29
  ## Usage
42
30
 
43
- ### Basic Usage
31
+ ### Basic usage
44
32
 
45
33
  ```typescript
46
34
  import { AsyncLRUCache } from '@wfp99/async-lru-cache';
47
35
 
48
- // Create a cache with maximum 100 items
49
- const cache = new AsyncLRUCache({
50
- capacity: 100
51
- });
36
+ const cache = new AsyncLRUCache<string, User>({ capacity: 100 });
52
37
 
53
- // Use get method to retrieve data
54
- const data = await cache.get('user:123', async () => {
55
- // Loader function executed on cache miss
56
- const response = await fetch('/api/users/123');
57
- return response.json();
38
+ // get() calls the loader on a cache miss and caches the resolved value.
39
+ const user = await cache.get('user:123', async () => {
40
+ const response = await fetch('/api/users/123');
41
+ return response.json();
58
42
  });
59
43
 
60
- // Use put method to store data
44
+ // put() stores a value directly and optionally persists it via a saver.
61
45
  await cache.put('user:456', userData, async (key, value) => {
62
- // Optional saver function for data persistence
63
- await saveToDatabase(key, value);
46
+ await saveToDatabase(key, value);
64
47
  });
65
48
  ```
66
49
 
67
- ### Error Handling
50
+ ### Read-only lookups
68
51
 
69
52
  ```typescript
70
- try {
71
- const data = await cache.get('problematic-key', async () => {
72
- throw new Error('Load failed');
73
- });
74
- } catch (error) {
75
- console.error('Cache load failed:', error);
76
- // Failed items are automatically removed from cache
77
- }
78
- ```
53
+ // get() without a loader performs a read-only lookup: on a hit it behaves like a
54
+ // normal get() (and updates LRU order); on a miss it resolves to undefined without
55
+ // adding anything to the cache or affecting LRU order.
56
+ const cached = await cache.get('user:123'); // User | undefined
79
57
 
80
- ### TTL (Time To Live) Support
58
+ // peek() never affects LRU order, whether it is a hit or a miss.
59
+ const peeked = await cache.peek('user:123'); // Promise<User> | undefined
60
+ ```
81
61
 
82
- AsyncLRUCache supports automatic expiration of cache entries using TTL (Time To Live):
62
+ ### TTL (time to live)
83
63
 
84
64
  ```typescript
85
- import { AsyncLRUCache } from '@wfp99/async-lru-cache';
86
-
87
- // Create cache with TTL support
88
65
  const cache = new AsyncLRUCache({
89
- capacity: 100,
90
- defaultTtlMs: 5000, // Default TTL of 5 seconds
91
- cleanupIntervalMs: 10000 // Cleanup expired entries every 10 seconds
66
+ capacity: 100,
67
+ defaultTtlMs: 5000, // default TTL applied to every entry
68
+ cleanupIntervalMs: 10000, // periodic background cleanup of expired entries
92
69
  });
93
70
 
94
- // Use default TTL
95
- const data1 = await cache.get('key1', async () => {
96
- return await fetchData();
97
- });
98
-
99
- // Override TTL for specific entries
100
- const data2 = await cache.get('key2', async () => {
101
- return await fetchCriticalData();
102
- }, 30000); // 30 seconds TTL
103
-
104
- // Put with custom TTL
105
- await cache.put('key3', value, async (key, val) => {
106
- await saveToDb(key, val);
107
- }, 60000); // 1 minute TTL
108
-
109
- // Manual cleanup of expired entries
110
- cache.cleanupExpired();
111
-
112
- // Check if key exists and is not expired
113
- if (cache.has('key1')) {
114
- console.log('Key exists and is valid');
115
- }
71
+ // Override the default TTL for a specific entry.
72
+ await cache.get('key1', async () => fetchData(), 30000);
116
73
 
117
- // Get cache size
118
- console.log('Current cache size:', cache.size());
119
-
120
- // Destroy cache (stops cleanup timer and clears all data)
121
- cache.destroy();
74
+ cache.has('key1'); // false once the entry has expired
75
+ cache.cleanupExpired(); // manually remove expired entries
76
+ cache.destroy(); // stop the cleanup timer and clear all data
122
77
  ```
123
78
 
124
- #### TTL Configuration Options
125
-
126
- - **`defaultTtlMs`**: Default TTL for all cache entries (optional)
127
- - **`cleanupIntervalMs`**: Automatic cleanup interval for expired entries (optional)
128
- - Individual `get()` and `put()` methods accept TTL overrides
129
- - Expired entries are automatically removed during normal operations
130
- - Call `cleanupExpired()` for manual cleanup
131
- - Call `destroy()` to stop timers and prevent memory leaks
132
-
133
- ### Advanced Usage
79
+ ### Concurrency handling
134
80
 
135
81
  ```typescript
136
- // Working with database
137
- const cache = new AsyncLRUCache<string, UserData>({ capacity: 500 });
138
-
139
- // Load from database with automatic caching
140
- const user = await cache.get(`user:${userId}`, async () => {
141
- return await database.users.findById(userId);
142
- });
82
+ // These concurrent get() calls share a single loader execution.
83
+ const [a, b, c] = await Promise.all([
84
+ cache.get('shared-key', loader),
85
+ cache.get('shared-key', loader),
86
+ cache.get('shared-key', loader),
87
+ ]);
143
88
 
144
- // Save to cache and database atomically
145
- await cache.put(`user:${userId}`, updatedUser, async (key, value) => {
146
- await database.users.update(userId, value);
147
- });
89
+ // These put() calls execute sequentially, even though they are started concurrently.
90
+ cache.put('key', 'value1', saver1);
91
+ cache.put('key', 'value2', saver2);
92
+ cache.put('key', 'value3', saver3);
148
93
  ```
149
94
 
150
- ### Cache Management
95
+ ### Error handling
151
96
 
152
- ```typescript
153
- // Manually remove specific cache entry
154
- cache.invalidate('user:123');
97
+ - If a loader (`get`) throws, the entry is removed from the cache and the rejection propagates to the caller.
98
+ - If a saver (`put`) throws, the entry is also removed to avoid caching an unpersisted value.
99
+ - A `put()` call ignores the failure of a previous, already-settled operation on the same key, so it can still proceed.
100
+ - By default, these failures are logged with `console.error`/`console.warn`. Provide `onError` to handle them yourself instead:
155
101
 
156
- // Clear all cache entries
157
- cache.clear();
102
+ ```typescript
103
+ const cache = new AsyncLRUCache({
104
+ capacity: 100,
105
+ onError(error, { key, source }) {
106
+ // source is 'loader' | 'saver' | 'put-chain'
107
+ myLogger.error(`cache ${source} failed for ${key}`, error);
108
+ },
109
+ });
158
110
  ```
159
111
 
160
112
  ## API Reference
161
113
 
162
114
  ### `AsyncLRUCache<K, V>`
163
115
 
164
- #### Constructor
165
-
166
116
  ```typescript
167
- constructor(option: AsyncLRUCacheOption)
117
+ constructor(option: AsyncLRUCacheOption<K>)
168
118
  ```
169
119
 
170
- #### `AsyncLRUCacheOption`
120
+ #### `AsyncLRUCacheOption<K>`
171
121
 
172
122
  | Property | Type | Required | Description |
173
- |----------|------|----------|-------------|
174
- | `capacity` | `number` | Yes | Maximum number of items allowed in cache. Must be a positive number. |
175
- | `defaultTtlMs` | `number` | No | Default TTL for all cache entries (optional) |
176
- | `cleanupIntervalMs` | `number` | No | Automatic cleanup interval for expired entries (optional) |
123
+ |---|---|---|---|
124
+ | `capacity` | `number` | Yes | Maximum number of items allowed in the cache. Must be at least 10. |
125
+ | `defaultTtlMs` | `number` | No | Default TTL applied to entries that don't specify their own. |
126
+ | `cleanupIntervalMs` | `number` | No | Interval for automatic background cleanup of expired entries. |
127
+ | `onError` | `(error: unknown, context: { key: K; source: 'loader' \| 'saver' \| 'put-chain' }) => void` | No | Callback for loader/saver failures. Replaces the default console logging when provided. |
177
128
 
178
129
  #### Methods
179
130
 
180
- ##### `get(key: K, loader: () => Promise<V>, ttlMs?: number): Promise<V>`
181
-
182
- Retrieves data from cache. If not found, uses the loader to load data.
183
-
184
- - **key**: Cache key
185
- - **loader**: Asynchronous loader function executed on cache miss
186
- - **ttlMs**: Optional TTL override for this entry
187
- - **Returns**: Promise that resolves to the required data
188
-
189
- ##### `put(key: K, value: V, saver?: (key: K, value: V) => Promise<void>, ttlMs?: number): Promise<V>`
190
-
191
- Puts a value into cache and optionally executes a saver function for persistence.
192
-
193
- - **key**: Cache key
194
- - **value**: Value to cache
195
- - **saver**: Optional asynchronous saver function
196
- - **ttlMs**: Optional TTL override for this entry
197
- - **Returns**: Promise that resolves to the latest value after saver operation completes
198
-
199
- ##### `invalidate(key: K): void`
131
+ | Method | Description |
132
+ |---|---|
133
+ | `get(key, loader, ttlMs?)` | Returns the cached value, or calls `loader()` on a miss and caches the result. |
134
+ | `get(key)` | Read-only lookup without a loader. On a hit, behaves like the overload above (updates LRU order). On a miss, resolves to `undefined` without touching the cache. |
135
+ | `peek(key)` | Returns the cached value's `Promise<V>`, or `undefined` if missing/expired. Never affects LRU order, even on a hit. |
136
+ | `put(key, value, saver?, ttlMs?)` | Stores `value` and optionally awaits `saver(key, value)` to persist it. |
137
+ | `invalidate(key)` | Removes the entry for `key`, if present. |
138
+ | `clear()` | Removes all entries. |
139
+ | `has(key)` | Returns whether `key` exists and has not expired. Expired entries are evicted as a side effect of this check; it does not affect LRU order. |
140
+ | `size()` | Returns the current number of entries. |
141
+ | `cleanupExpired()` | Immediately removes all expired entries. |
142
+ | `destroy()` | Stops the cleanup timer (if any) and clears all data. |
200
143
 
201
- Invalidates and removes the cache entry for the specified key.
144
+ ## TypeScript
202
145
 
203
- - **key**: Cache key to invalidate
204
-
205
- ##### `clear(): void`
206
-
207
- Clears all cache entries. This method removes all items from cache and manually clears node links to prevent potential memory leaks.
208
-
209
- ##### `has(key: K): boolean`
210
-
211
- Checks if a key exists in cache and is not expired.
212
-
213
- - **key**: Cache key to check
214
- - **Returns**: True if key exists and is valid
215
-
216
- ##### `size(): number`
217
-
218
- Returns the current number of items in cache.
219
-
220
- - **Returns**: Number of cached items
221
-
222
- ##### `cleanupExpired(): void`
223
-
224
- Manually removes expired entries from cache.
225
-
226
- ##### `destroy(): void`
227
-
228
- Destroys the cache, stops cleanup timers and clears all data.
229
-
230
- ## Concurrency Handling
231
-
232
- AsyncLRUCache automatically handles concurrent operations:
233
-
234
- - **GET Request Merging**: Multiple concurrent requests for the same key share a single loader execution
235
- - **PUT Operation Serialization**: Multiple PUT operations for the same key are serialized to ensure they execute in order
236
-
237
- ```typescript
238
- // These three concurrent requests will share the same loader execution
239
- const [data1, data2, data3] = await Promise.all([
240
- cache.get('shared-key', loader),
241
- cache.get('shared-key', loader),
242
- cache.get('shared-key', loader)
243
- ]);
244
-
245
- // These operations will execute sequentially, even if started concurrently
246
- cache.put('key', 'value1', saver1);
247
- cache.put('key', 'value2', saver2);
248
- cache.put('key', 'value3', saver3);
249
- ```
250
-
251
- ## TypeScript Support
252
-
253
- This package is fully written in TypeScript and provides complete type support:
254
-
255
- ```typescript
256
- interface User {
257
- id: string;
258
- name: string;
259
- email: string;
260
- }
261
-
262
- const userCache = new AsyncLRUCache<string, User>({
263
- capacity: 1000
264
- });
265
-
266
- const user: User = await userCache.get('user:123', async () => {
267
- // Loader must return User type
268
- return fetchUserFromAPI('123');
269
- });
270
- ```
271
-
272
- ## Error Handling
273
-
274
- - **Loader Failures**: If loader function throws an exception, corresponding cache entry is automatically removed
275
- - **Saver Failures**: If saver function fails, cache entry is also removed to ensure data consistency
276
- - **Operation Chain Errors**: PUT operations ignore previous operation errors, allowing new operations to proceed
277
- - **Error Logging**: All errors are automatically logged to console for debugging
146
+ This package is written in TypeScript and ships with type declarations; no separate `@types` package is required.
278
147
 
279
148
  ## License
280
149
 
@@ -284,6 +153,3 @@ MIT
284
153
 
285
154
  Issues and pull requests are welcome on [GitHub](https://github.com/wfp99/async-lru-cache).
286
155
 
287
- ## Author
288
-
289
- Wang Feng Ping
package/README.zh-TW.md CHANGED
@@ -4,29 +4,17 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Node.js Version](https://img.shields.io/badge/node-%3E%3D14.0.0-brightgreen.svg)](https://nodejs.org/)
6
6
 
7
- ไธ€ๅ€‹ๆ”ฏๆด้žๅŒๆญฅ่ผ‰ๅ…ฅๅ’Œไฟๅญ˜ๆ“ไฝœ็š„ LRU๏ผˆLeast Recently Used๏ผ‰่จ˜ๆ†ถ้ซ”ๅฟซๅ–๏ผŒ่‡ชๅ‹•่™•็†ไธฆ็™ผ่ซ‹ๆฑ‚ใ€‚
7
+ ไธ€ๅ€‹ๆ”ฏๆด้žๅŒๆญฅ่ผ‰ๅ…ฅ่ˆ‡ไฟๅญ˜ๆ“ไฝœใ€ไธฆ่‡ชๅ‹•่™•็†ไธฆ็™ผ่ซ‹ๆฑ‚็š„ LRU๏ผˆLeast Recently Used๏ผ‰่จ˜ๆ†ถ้ซ”ๅฟซๅ–๏ผŒ้ฉ็”จๆ–ผ Node.js ่ˆ‡ TypeScriptใ€‚
8
8
 
9
9
  ## ็‰น่‰ฒ
10
10
 
11
- - ๐Ÿš€ **้žๅŒๆญฅๆ”ฏๆด**๏ผšๆ”ฏๆด้žๅŒๆญฅ็š„่ผ‰ๅ…ฅๅ™จ๏ผˆloader๏ผ‰ๅ’Œไฟๅญ˜ๅ™จ๏ผˆsaver๏ผ‰ๅ‡ฝๆ•ธ
12
- - ๐Ÿ”„ **่‡ชๅ‹•ๅˆไฝต**๏ผš่‡ชๅ‹•ๅˆไฝต็›ธๅŒ key ็š„ไธฆ็™ผ GET ่ซ‹ๆฑ‚
13
- - ๐Ÿ“ **ๅบๅˆ—ๅŒ–ๅฏซๅ…ฅ**๏ผšๅบๅˆ—ๅŒ–ๅŒไธ€ key ็š„ PUT ๆ“ไฝœ๏ผŒ็ขบไฟ้ †ๅบๅŸท่กŒ
14
- - ๐Ÿ—‘๏ธ **LRU ๆท˜ๆฑฐ็ญ–็•ฅ**๏ผšๅฏฆ็พ LRU ๆผ”็ฎ—ๆณ•๏ผŒ่‡ชๅ‹•็งป้™คๆœ€ไน…ๆœชไฝฟ็”จ็š„้ …็›ฎ
15
- - โฐ **TTL ๆ”ฏๆด**๏ผšๆ”ฏๆดๅฏ้…็ฝฎ็š„็”Ÿๅญ˜ๆ™‚้–“๏ผˆTime To Live๏ผ‰๏ผŒ่‡ชๅ‹•้ŽๆœŸๅฟซๅ–้ …็›ฎ
16
- - ๐Ÿ›ก๏ธ **้Œฏ่ชค่™•็†**๏ผšๅฎŒๅ–„็š„้Œฏ่ชค่™•็†ๆฉŸๅˆถ๏ผŒๅคฑๆ•—ๆ™‚่‡ชๅ‹•ๆธ…็†ๅฟซๅ–
17
- - ๐Ÿงน **ๅฎŒๆ•ดๆธ…้™ค**๏ผšๆ”ฏๆดไธ€ๆฌกๆ€งๆธ…้™คๆ‰€ๆœ‰ๅฟซๅ–้ …็›ฎ
18
-
19
- ## ็‚บไป€้บผ้ธๆ“‡่จ˜ๆ†ถ้ซ”ๅ…งๅฟซๅ–่€Œ้žๅค–้ƒจๅฟซๅ–ๆœๅ‹™๏ผŸ
20
-
21
- **็ฐกๅ–ฎๆ€ง่ˆ‡ๆ•ˆ่ƒฝ**
22
- - ็„ก้œ€่จญๅฎš - ๅฎ‰่ฃๅพŒ็ซ‹ๅณไฝฟ็”จ
23
- - ้›ถ็ถฒ่ทฏๅปถ้ฒ - ็›ดๆŽฅ่จ˜ๆ†ถ้ซ”ๅญ˜ๅ–
24
- - ๆธ›ๅฐ‘ๅŸบ็คŽๆžถๆง‹่ค‡้›œๆ€ง
25
-
26
- **ๅฏ้ ๆ€ง่ˆ‡้–‹็™ผ**
27
- - ็„กๅค–้ƒจไพ่ณดๆˆ–็ถฒ่ทฏๆ•…้šœ้ขจ้šช
28
- - ็ฐกๅŒ–ๆธฌ่ฉฆๅ’Œ้™ค้Œฏ
29
- - ็„ก้œ€ Redisใ€Memcached ๆˆ–ๅ…ถไป–ๅฟซๅ–ไผบๆœๅ™จ
11
+ - ๆ”ฏๆด้žๅŒๆญฅ็š„่ผ‰ๅ…ฅๅ™จ๏ผˆloader๏ผ‰่ˆ‡ไฟๅญ˜ๅ™จ๏ผˆsaver๏ผ‰ๅ‡ฝๆ•ธ
12
+ - ๅฐๅŒไธ€ key ็š„ไธฆ็™ผ `get()` ๅ‘ผๅซๅ…ฑ็”จๅ–ฎไธ€่ผ‰ๅ…ฅๅ™จๅŸท่กŒ
13
+ - ๅฐๅŒไธ€ key ็š„ๅคšๅ€‹ `put()` ๅ‘ผๅซๆœƒๅบๅˆ—ๅŒ–ไธฆไพๅบๅŸท่กŒ
14
+ - ่ถ…ๅ‡บ่จญๅฎšๅฎน้‡ๆ™‚่‡ชๅ‹•ๅŸท่กŒ LRU ๆท˜ๆฑฐ
15
+ - ๆ”ฏๆดๆฏ็ญ†้ …็›ฎ็š„ๅฏ้ธ TTL๏ผˆๅญ˜ๆดปๆ™‚้–“๏ผ‰๏ผŒไธฆๅฏ่จญๅฎš้€ฑๆœŸๆ€ง่ƒŒๆ™ฏๆธ…็†
16
+ - ๅฏ้€้Ž `onError` ่‡ช่จ‚้Œฏ่ชค่™•็†๏ผŒ้ ่จญ็‚บ่จ˜้Œ„ๅˆฐ console
17
+ - `get()` ๅฏ็œ็•ฅ loader ้€ฒ่กŒๅ”ฏ่ฎ€ๆŸฅ่ฉข๏ผ›`peek()` ๅ‰‡ๆไพ›ๅฎŒๅ…จไธๅฝฑ้Ÿฟ LRU ้ †ๅบ็š„ๆŸฅ่ฉขๆ–นๅผ
30
18
 
31
19
  ## ๅฎ‰่ฃ
32
20
 
@@ -45,236 +33,116 @@ npm install @wfp99/async-lru-cache
45
33
  ```typescript
46
34
  import { AsyncLRUCache } from '@wfp99/async-lru-cache';
47
35
 
48
- // ๅ‰ตๅปบไธ€ๅ€‹ๆœ€ๅคšๅฎน็ด 100 ๅ€‹้ …็›ฎ็š„ๅฟซๅ–
49
- const cache = new AsyncLRUCache({
50
- capacity: 100
51
- });
36
+ const cache = new AsyncLRUCache<string, User>({ capacity: 100 });
52
37
 
53
- // ไฝฟ็”จ get ๆ–นๆณ•็ฒๅ–่ณ‡ๆ–™
54
- const data = await cache.get('user:123', async () => {
55
- // ๅฟซๅ–ๆœชๅ‘ฝไธญๆ™‚ๅŸท่กŒ็š„่ผ‰ๅ…ฅๅ™จๅ‡ฝๆ•ธ
56
- const response = await fetch('/api/users/123');
57
- return response.json();
38
+ // get() ๅœจๅฟซๅ–ๆœชๅ‘ฝไธญๆ™‚ๅ‘ผๅซ loader๏ผŒไธฆๅฟซๅ–ๅ…ถๅ›žๅ‚ณๅ€ผใ€‚
39
+ const user = await cache.get('user:123', async () => {
40
+ const response = await fetch('/api/users/123');
41
+ return response.json();
58
42
  });
59
43
 
60
- // ไฝฟ็”จ put ๆ–นๆณ•ๅ„ฒๅญ˜่ณ‡ๆ–™
44
+ // put() ็›ดๆŽฅๅ„ฒๅญ˜ๅ€ผ๏ผŒไธฆๅฏ้ธๆ“‡้€้Ž saver ้€ฒ่กŒๆŒไน…ๅŒ–ใ€‚
61
45
  await cache.put('user:456', userData, async (key, value) => {
62
- // ๅฏ้ธ็š„ไฟๅญ˜ๅ™จๅ‡ฝๆ•ธ๏ผŒ็”จๆ–ผๆŒไน…ๅŒ–่ณ‡ๆ–™
63
- await saveToDatabase(key, value);
46
+ await saveToDatabase(key, value);
64
47
  });
65
48
  ```
66
49
 
67
- ### ้Œฏ่ชค่™•็†
50
+ ### ๅ”ฏ่ฎ€ๆŸฅ่ฉข
68
51
 
69
52
  ```typescript
70
- try {
71
- const data = await cache.get('problematic-key', async () => {
72
- throw new Error('่ผ‰ๅ…ฅๅคฑๆ•—');
73
- });
74
- } catch (error) {
75
- console.error('ๅฟซๅ–่ผ‰ๅ…ฅๅคฑๆ•—:', error);
76
- // ๅคฑๆ•—็š„้ …็›ฎๆœƒ่‡ชๅ‹•ๅพžๅฟซๅ–ไธญ็งป้™ค
77
- }
78
- ```
53
+ // ็œ็•ฅ loader ็š„ get() ๅฑฌๆ–ผๅ”ฏ่ฎ€ๆŸฅ่ฉข๏ผšๅ‘ฝไธญๆ™‚่กŒ็‚บ่ˆ‡ไธ€่ˆฌ get() ็›ธๅŒ๏ผˆๆœƒๆ›ดๆ–ฐ LRU ้ †ๅบ๏ผ‰๏ผ›
54
+ // ๆœชๅ‘ฝไธญๆ™‚ๅ›žๅ‚ณ undefined๏ผŒไธ”ไธๆœƒๆ–ฐๅขžไปปไฝ•้ …็›ฎๆˆ–ๅฝฑ้Ÿฟ LRU ้ †ๅบใ€‚
55
+ const cached = await cache.get('user:123'); // User | undefined
79
56
 
80
- ### TTL๏ผˆ็”Ÿๅญ˜ๆ™‚้–“๏ผ‰ๆ”ฏๆด
57
+ // peek() ็„ก่ซ–ๅ‘ฝไธญๆˆ–ๆœชๅ‘ฝไธญ้ƒฝไธๆœƒๅฝฑ้Ÿฟ LRU ้ †ๅบใ€‚
58
+ const peeked = await cache.peek('user:123'); // Promise<User> | undefined
59
+ ```
81
60
 
82
- AsyncLRUCache ๆ”ฏๆดไฝฟ็”จ TTL๏ผˆTime To Live๏ผ‰่‡ชๅ‹•้ŽๆœŸๅฟซๅ–้ …็›ฎ๏ผš
61
+ ### TTL๏ผˆๅญ˜ๆดปๆ™‚้–“๏ผ‰
83
62
 
84
63
  ```typescript
85
- import { AsyncLRUCache } from '@wfp99/async-lru-cache';
86
-
87
- // ๅปบ็ซ‹ๆ”ฏๆด TTL ็š„ๅฟซๅ–
88
64
  const cache = new AsyncLRUCache({
89
- capacity: 100,
90
- defaultTtlMs: 5000, // ้ ่จญ TTL ็‚บ 5 ็ง’
91
- cleanupIntervalMs: 10000 // ๆฏ 10 ็ง’ๆธ…็†้ŽๆœŸ้ …็›ฎ
92
- });
93
-
94
- // ไฝฟ็”จ้ ่จญ TTL
95
- const data1 = await cache.get('key1', async () => {
96
- return await fetchData();
65
+ capacity: 100,
66
+ defaultTtlMs: 5000, // ๅฅ—็”จๅˆฐๆฏ็ญ†้ …็›ฎ็š„้ ่จญ TTL
67
+ cleanupIntervalMs: 10000, // ้€ฑๆœŸๆ€ง่ƒŒๆ™ฏๆธ…็†้ŽๆœŸ้ …็›ฎ
97
68
  });
98
69
 
99
- // ็‚บ็‰นๅฎš้ …็›ฎ่ฆ†ๅฏซ TTL
100
- const data2 = await cache.get('key2', async () => {
101
- return await fetchCriticalData();
102
- }, 30000); // 30 ็ง’ TTL
103
-
104
- // ไฝฟ็”จ่‡ช่จ‚ TTL ้€ฒ่กŒ Put ๆ“ไฝœ
105
- await cache.put('key3', value, async (key, val) => {
106
- await saveToDb(key, val);
107
- }, 60000); // 1 ๅˆ†้˜ TTL
108
-
109
- // ๆ‰‹ๅ‹•ๆธ…็†้ŽๆœŸ้ …็›ฎ
110
- cache.cleanupExpired();
70
+ // ็‚บ็‰นๅฎš้ …็›ฎ่ฆ†ๅฏซ้ ่จญ TTLใ€‚
71
+ await cache.get('key1', async () => fetchData(), 30000);
111
72
 
112
- // ๆชขๆŸฅ key ๆ˜ฏๅฆๅญ˜ๅœจไธ”ๆœช้ŽๆœŸ
113
- if (cache.has('key1')) {
114
- console.log('Key ๅญ˜ๅœจไธ”ๆœ‰ๆ•ˆ');
115
- }
116
-
117
- // ๅ–ๅพ—ๅฟซๅ–ๅคงๅฐ
118
- console.log('็›ฎๅ‰ๅฟซๅ–ๅคงๅฐ:', cache.size());
119
-
120
- // ้Šทๆฏ€ๅฟซๅ–๏ผˆๅœๆญขๆธ…็†่จˆๆ™‚ๅ™จไธฆๆธ…้™คๆ‰€ๆœ‰่ณ‡ๆ–™๏ผ‰
121
- cache.destroy();
73
+ cache.has('key1'); // ้ …็›ฎ้ŽๆœŸๅพŒๅ›žๅ‚ณ false
74
+ cache.cleanupExpired(); // ๆ‰‹ๅ‹•็งป้™ค้ŽๆœŸ้ …็›ฎ
75
+ cache.destroy(); // ๅœๆญขๆธ…็†่จˆๆ™‚ๅ™จไธฆๆธ…้™คๆ‰€ๆœ‰่ณ‡ๆ–™
122
76
  ```
123
77
 
124
- #### TTL ้…็ฝฎ้ธ้ …
125
-
126
- - **`defaultTtlMs`**๏ผšๆ‰€ๆœ‰ๅฟซๅ–้ …็›ฎ็š„้ ่จญ TTL๏ผˆๅฏ้ธ๏ผ‰
127
- - **`cleanupIntervalMs`**๏ผš้ŽๆœŸ้ …็›ฎ็š„่‡ชๅ‹•ๆธ…็†้–“้š”๏ผˆๅฏ้ธ๏ผ‰
128
- - ๅ€‹ๅˆฅ็š„ `get()` ๅ’Œ `put()` ๆ–นๆณ•ๆŽฅๅ— TTL ่ฆ†ๅฏซ
129
- - ้ŽๆœŸ้ …็›ฎๅœจๆญฃๅธธๆ“ไฝœๆœŸ้–“ๆœƒ่‡ชๅ‹•็งป้™ค
130
- - ๅ‘ผๅซ `cleanupExpired()` ้€ฒ่กŒๆ‰‹ๅ‹•ๆธ…็†
131
- - ๅ‘ผๅซ `destroy()` ๅœๆญข่จˆๆ™‚ๅ™จไธฆ้˜ฒๆญข่จ˜ๆ†ถ้ซ”ๆดฉๆผ
132
-
133
- ### ้€ฒ้šŽไฝฟ็”จ
78
+ ### ไธฆ็™ผ่™•็†
134
79
 
135
80
  ```typescript
136
- // ่ˆ‡่ณ‡ๆ–™ๅบซ้…ๅˆไฝฟ็”จ
137
- const cache = new AsyncLRUCache<string, UserData>({ capacity: 500 });
138
-
139
- // ๅพž่ณ‡ๆ–™ๅบซ่ผ‰ๅ…ฅไธฆ่‡ชๅ‹•ๅฟซๅ–
140
- const user = await cache.get(`user:${userId}`, async () => {
141
- return await database.users.findById(userId);
142
- });
81
+ // ไปฅไธ‹ไธฆ็™ผ็š„ get() ๅ‘ผๅซๆœƒๅ…ฑ็”จๅŒไธ€ๅ€‹ loader ๅŸท่กŒใ€‚
82
+ const [a, b, c] = await Promise.all([
83
+ cache.get('shared-key', loader),
84
+ cache.get('shared-key', loader),
85
+ cache.get('shared-key', loader),
86
+ ]);
143
87
 
144
- // ๅŽŸๅญๆ€งๅœฐไฟๅญ˜ๅˆฐๅฟซๅ–ๅ’Œ่ณ‡ๆ–™ๅบซ
145
- await cache.put(`user:${userId}`, updatedUser, async (key, value) => {
146
- await database.users.update(userId, value);
147
- });
88
+ // ไปฅไธ‹ put() ๅ‘ผๅซๅณไฝฟไธฆ็™ผๅ•Ÿๅ‹•๏ผŒไนŸๆœƒไพๅบๅบๅˆ—ๅŒ–ๅŸท่กŒใ€‚
89
+ cache.put('key', 'value1', saver1);
90
+ cache.put('key', 'value2', saver2);
91
+ cache.put('key', 'value3', saver3);
148
92
  ```
149
93
 
150
- ### ๅฟซๅ–็ฎก็†
94
+ ### ้Œฏ่ชค่™•็†
151
95
 
152
- ```typescript
153
- // ๆ‰‹ๅ‹•็งป้™ค็‰นๅฎšๅฟซๅ–้ …็›ฎ
154
- cache.invalidate('user:123');
96
+ - ่‹ฅ่ผ‰ๅ…ฅๅ™จ๏ผˆ`get`๏ผ‰ๆ‹‹ๅ‡บไพ‹ๅค–๏ผŒ่ฉฒ้ …็›ฎๆœƒๅพžๅฟซๅ–ไธญ็งป้™ค๏ผŒ้Œฏ่ชคๆœƒๅ‚ณ้ž็ตฆๅ‘ผๅซ็ซฏใ€‚
97
+ - ่‹ฅไฟๅญ˜ๅ™จ๏ผˆ`put`๏ผ‰ๆ‹‹ๅ‡บไพ‹ๅค–๏ผŒ่ฉฒ้ …็›ฎไนŸๆœƒ่ขซ็งป้™ค๏ผŒ้ฟๅ…ๅฟซๅ–ๅˆฐๅฐšๆœชๆŒไน…ๅŒ–็š„ๅ€ผใ€‚
98
+ - `put()` ๆœƒๅฟฝ็•ฅๅŒไธ€ key ๅ‰ไธ€ๅ€‹ๅทฒ็ตๆŸๆ“ไฝœ็š„ๅคฑๆ•—๏ผŒ่ฎ“ๆ–ฐ็š„ๆ“ไฝœๅฏไปฅ็นผ็บŒ้€ฒ่กŒใ€‚
99
+ - ้ ่จญๆƒ…ๆณไธ‹๏ผŒไธŠ่ฟฐๅคฑๆ•—ๆœƒ้€้Ž `console.error`/`console.warn` ่จ˜้Œ„ใ€‚่‹ฅ่ฆ่‡ช่กŒ่™•็†๏ผŒๅฏๆไพ› `onError`๏ผš
155
100
 
156
- // ๆธ…้™คๆ‰€ๆœ‰ๅฟซๅ–้ …็›ฎ
157
- cache.clear();
101
+ ```typescript
102
+ const cache = new AsyncLRUCache({
103
+ capacity: 100,
104
+ onError(error, { key, source }) {
105
+ // source ็‚บ 'loader' | 'saver' | 'put-chain'
106
+ myLogger.error(`cache ${source} failed for ${key}`, error);
107
+ },
108
+ });
158
109
  ```
159
110
 
160
111
  ## API ๅƒ่€ƒ
161
112
 
162
113
  ### `AsyncLRUCache<K, V>`
163
114
 
164
- #### ๅปบๆง‹ๅ‡ฝๆ•ธ
165
-
166
115
  ```typescript
167
- constructor(option: AsyncLRUCacheOption)
116
+ constructor(option: AsyncLRUCacheOption<K>)
168
117
  ```
169
118
 
170
- #### `AsyncLRUCacheOption`
119
+ #### `AsyncLRUCacheOption<K>`
171
120
 
172
121
  | ๅฑฌๆ€ง | ้กžๅž‹ | ๅฟ…้œ€ | ๆ่ฟฐ |
173
- |-----|------|------|------|
174
- | `capacity` | `number` | ๆ˜ฏ | ๅฟซๅ–ไธญๅ…่จฑ็š„ๆœ€ๅคง้ …็›ฎๆ•ธ้‡๏ผŒๅฟ…้ ˆ็‚บๆญฃๆ•ธ |
175
- | `defaultTtlMs` | `number` | ๅฆ | ๆ‰€ๆœ‰ๅฟซๅ–้ …็›ฎ็š„้ ่จญ TTL๏ผˆๅฏ้ธ๏ผ‰ |
176
- | `cleanupIntervalMs` | `number` | ๅฆ | ้ŽๆœŸ้ …็›ฎ็š„่‡ชๅ‹•ๆธ…็†้–“้š”๏ผˆๅฏ้ธ๏ผ‰ |
122
+ |---|---|---|---|
123
+ | `capacity` | `number` | ๆ˜ฏ | ๅฟซๅ–ไธญๅ…่จฑ็š„ๆœ€ๅคง้ …็›ฎๆ•ธ้‡๏ผŒๆœ€ๅฐๅ€ผ็‚บ 10 |
124
+ | `defaultTtlMs` | `number` | ๅฆ | ๅฅ—็”จๅˆฐๆœชๆŒ‡ๅฎš TTL ไน‹้ …็›ฎ็š„้ ่จญ TTL |
125
+ | `cleanupIntervalMs` | `number` | ๅฆ | ่‡ชๅ‹•่ƒŒๆ™ฏๆธ…็†้ŽๆœŸ้ …็›ฎ็š„ๅŸท่กŒ้–“้š” |
126
+ | `onError` | `(error: unknown, context: { key: K; source: 'loader' \| 'saver' \| 'put-chain' }) => void` | ๅฆ | ่ผ‰ๅ…ฅๅ™จ/ไฟๅญ˜ๅ™จๅคฑๆ•—ๆ™‚็š„ๅ›žๅ‘ผๅ‡ฝๆ•ธใ€‚ๆไพ›ๅพŒๅฐ‡ๅ–ไปฃ้ ่จญ็š„ console ่จ˜้Œ„่กŒ็‚บ |
177
127
 
178
128
  #### ๆ–นๆณ•
179
129
 
180
- ##### `get(key: K, loader: () => Promise<V>, ttlMs?: number): Promise<V>`
181
-
182
- ๅพžๅฟซๅ–ไธญ็ฒๅ–่ณ‡ๆ–™ใ€‚ๅฆ‚ๆžœไธๅญ˜ๅœจ๏ผŒไฝฟ็”จ่ผ‰ๅ…ฅๅ™จ่ผ‰ๅ…ฅ่ณ‡ๆ–™ใ€‚
183
-
184
- - **key**: ๅฟซๅ–้ต
185
- - **loader**: ๅฟซๅ–ๆœชๅ‘ฝไธญๆ™‚ๅŸท่กŒ็š„้žๅŒๆญฅ่ผ‰ๅ…ฅๅ‡ฝๆ•ธ
186
- - **ttlMs**: ๆญค้ …็›ฎ็š„ๅฏ้ธ TTL ่ฆ†ๅฏซ
187
- - **ๅ›žๅ‚ณ**: Promise๏ผŒ่งฃๆž็‚บๆ‰€้œ€็š„่ณ‡ๆ–™
188
-
189
- ##### `put(key: K, value: V, saver?: (key: K, value: V) => Promise<void>, ttlMs?: number): Promise<V>`
190
-
191
- ๅฐ‡ๅ€ผๆ”พๅ…ฅๅฟซๅ–ไธญ๏ผŒไธฆๅฏ้ธๆ“‡ๆ€งๅœฐๅŸท่กŒไฟๅญ˜ๅ™จๅ‡ฝๆ•ธ้€ฒ่กŒๆŒไน…ๅŒ–ใ€‚
192
-
193
- - **key**: ๅฟซๅ–้ต
194
- - **value**: ่ฆๅฟซๅ–็š„ๅ€ผ
195
- - **saver**: ๅฏ้ธ็š„้žๅŒๆญฅไฟๅญ˜ๅ‡ฝๆ•ธ
196
- - **ttlMs**: ๆญค้ …็›ฎ็š„ๅฏ้ธ TTL ่ฆ†ๅฏซ
197
- - **ๅ›žๅ‚ณ**: Promise๏ผŒ่งฃๆž็‚บไฟๅญ˜ๆ“ไฝœๅฎŒๆˆๅพŒ็š„ๆœ€ๆ–ฐๅ€ผ
130
+ | ๆ–นๆณ• | ๆ่ฟฐ |
131
+ |---|---|
132
+ | `get(key, loader, ttlMs?)` | ๅ›žๅ‚ณๅฟซๅ–ๅ€ผ๏ผ›ๆœชๅ‘ฝไธญๆ™‚ๅ‘ผๅซ `loader()` ไธฆๅฟซๅ–ๅ…ถ็ตๆžœ |
133
+ | `get(key)` | ็œ็•ฅ loader ็š„ๅ”ฏ่ฎ€ๆŸฅ่ฉขใ€‚ๅ‘ฝไธญๆ™‚่กŒ็‚บ่ˆ‡ไธŠๆ–นๅคš่ผ‰็›ธๅŒ๏ผˆๆœƒๆ›ดๆ–ฐ LRU ้ †ๅบ๏ผ‰๏ผ›ๆœชๅ‘ฝไธญๆ™‚ๅ›žๅ‚ณ `undefined`๏ผŒไธๆœƒ็•ฐๅ‹•ๅฟซๅ– |
134
+ | `peek(key)` | ๅ›žๅ‚ณๅฟซๅ–ๅ€ผ็š„ `Promise<V>`๏ผ›่‹ฅไธๅญ˜ๅœจๆˆ–ๅทฒ้ŽๆœŸๅ‰‡ๅ›žๅ‚ณ `undefined`ใ€‚็„ก่ซ–ๅ‘ฝไธญ่ˆ‡ๅฆ้ƒฝไธๅฝฑ้Ÿฟ LRU ้ †ๅบ |
135
+ | `put(key, value, saver?, ttlMs?)` | ๅ„ฒๅญ˜ `value`๏ผŒไธฆๅฏ้ธๆ“‡็ญ‰ๅพ… `saver(key, value)` ้€ฒ่กŒๆŒไน…ๅŒ– |
136
+ | `invalidate(key)` | ่‹ฅๅญ˜ๅœจ๏ผŒ็งป้™ค `key` ๅฐๆ‡‰็š„้ …็›ฎ |
137
+ | `clear()` | ็งป้™คๆ‰€ๆœ‰้ …็›ฎ |
138
+ | `has(key)` | ๅ›žๅ‚ณ `key` ๆ˜ฏๅฆๅญ˜ๅœจไธ”ๆœช้ŽๆœŸใ€‚ๆญคๆชขๆŸฅๆœƒ้ †ๅธถๆธ…้™คๅทฒ้ŽๆœŸ็š„้ …็›ฎ๏ผŒไฝ†ไธๅฝฑ้Ÿฟ LRU ้ †ๅบ |
139
+ | `size()` | ๅ›žๅ‚ณ็›ฎๅ‰็š„้ …็›ฎๆ•ธ้‡ |
140
+ | `cleanupExpired()` | ็ซ‹ๅณ็งป้™คๆ‰€ๆœ‰้ŽๆœŸ้ …็›ฎ |
141
+ | `destroy()` | ๅœๆญขๆธ…็†่จˆๆ™‚ๅ™จ๏ผˆ่‹ฅๆœ‰๏ผ‰ไธฆๆธ…้™คๆ‰€ๆœ‰่ณ‡ๆ–™ |
198
142
 
199
- ##### `invalidate(key: K): void`
143
+ ## TypeScript
200
144
 
201
- ไฝฟๆŒ‡ๅฎš้ต็š„ๅฟซๅ–้ …็›ฎๅคฑๆ•ˆไธฆ็งป้™คใ€‚
202
-
203
- - **key**: ่ฆๅคฑๆ•ˆ็š„ๅฟซๅ–้ต
204
-
205
- ##### `clear(): void`
206
-
207
- ๆธ…้™คๆ‰€ๆœ‰ๅฟซๅ–้ …็›ฎใ€‚ๆญคๆ–นๆณ•ๆœƒ็งป้™คๅฟซๅ–ไธญ็š„ๆ‰€ๆœ‰้ …็›ฎไธฆๆ‰‹ๅ‹•ๆธ…็†็ฏ€้ปž้€ฃ็ตไปฅ้ฟๅ…ๆฝ›ๅœจ็š„่จ˜ๆ†ถ้ซ”ๆดฉๆผใ€‚
208
-
209
- ##### `has(key: K): boolean`
210
-
211
- ๆชขๆŸฅๅฟซๅ–ไธญๆ˜ฏๅฆๅญ˜ๅœจๆŒ‡ๅฎš้ตไธ”ๆœช้ŽๆœŸใ€‚
212
-
213
- - **key**: ่ฆๆชขๆŸฅ็š„ๅฟซๅ–้ต
214
- - **ๅ›žๅ‚ณ**: ๅฆ‚ๆžœ้ตๅญ˜ๅœจไธ”ๆœ‰ๆ•ˆๅ‰‡ๅ›žๅ‚ณ true
215
-
216
- ##### `size(): number`
217
-
218
- ๅ›žๅ‚ณๅฟซๅ–ไธญ็›ฎๅ‰็š„้ …็›ฎๆ•ธ้‡ใ€‚
219
-
220
- - **ๅ›žๅ‚ณ**: ๅฟซๅ–้ …็›ฎ็š„ๆ•ธ้‡
221
-
222
- ##### `cleanupExpired(): void`
223
-
224
- ๆ‰‹ๅ‹•ๅพžๅฟซๅ–ไธญ็งป้™ค้ŽๆœŸ้ …็›ฎใ€‚
225
-
226
- ##### `destroy(): void`
227
-
228
- ้Šทๆฏ€ๅฟซๅ–๏ผŒๅœๆญขๆธ…็†่จˆๆ™‚ๅ™จไธฆๆธ…้™คๆ‰€ๆœ‰่ณ‡ๆ–™ใ€‚
229
-
230
- ## ไธฆ็™ผ่™•็†
231
-
232
- AsyncLRUCache ๆœƒ่‡ชๅ‹•่™•็†ไธฆ็™ผๆ“ไฝœ๏ผš
233
-
234
- - **GET ่ซ‹ๆฑ‚ๅˆไฝต**๏ผšๅฐๅŒไธ€ key ็š„ๅคšๅ€‹ไธฆ็™ผ่ซ‹ๆฑ‚ๆœƒๅ…ฑไบซๅ–ฎไธ€่ผ‰ๅ…ฅๅ™จๅŸท่กŒ
235
- - **PUT ๆ“ไฝœๅบๅˆ—ๅŒ–**๏ผšๅฐๅŒไธ€ key ็š„ๅคšๅ€‹ PUT ๆ“ไฝœๆœƒๅบๅˆ—ๅŒ–ๅŸท่กŒไปฅ็ขบไฟ้ †ๅบ
236
-
237
- ```typescript
238
- // ้€™ไธ‰ๅ€‹ไธฆ็™ผ่ซ‹ๆฑ‚ๆœƒๅ…ฑไบซๅŒไธ€ๅ€‹่ผ‰ๅ…ฅๅ™จๅŸท่กŒ
239
- const [data1, data2, data3] = await Promise.all([
240
- cache.get('shared-key', loader),
241
- cache.get('shared-key', loader),
242
- cache.get('shared-key', loader)
243
- ]);
244
-
245
- // ้€™ไบ›ๆ“ไฝœๆœƒๆŒ‰้ †ๅบๅŸท่กŒ๏ผŒๅณไฝฟๅฎƒๅ€‘ๆ˜ฏไธฆ็™ผๅ•Ÿๅ‹•็š„
246
- cache.put('key', 'value1', saver1);
247
- cache.put('key', 'value2', saver2);
248
- cache.put('key', 'value3', saver3);
249
- ```
250
-
251
- ## TypeScript ๆ”ฏๆด
252
-
253
- ๆญคๅฅ—ไปถๅฎŒๅ…จไฝฟ็”จ TypeScript ็ทจๅฏซ๏ผŒๆไพ›ๅฎŒๆ•ด็š„ๅž‹ๅˆฅๆ”ฏๆด๏ผš
254
-
255
- ```typescript
256
- interface User {
257
- id: string;
258
- name: string;
259
- email: string;
260
- }
261
-
262
- const userCache = new AsyncLRUCache<string, User>({
263
- capacity: 1000
264
- });
265
-
266
- const user: User = await userCache.get('user:123', async () => {
267
- // ่ผ‰ๅ…ฅๅ™จๅฟ…้ ˆๅ›žๅ‚ณ User ๅž‹ๅˆฅ
268
- return fetchUserFromAPI('123');
269
- });
270
- ```
271
-
272
- ## ้Œฏ่ชค่™•็†
273
-
274
- - **่ผ‰ๅ…ฅๅ™จๅคฑๆ•—**๏ผšๅฆ‚ๆžœ่ผ‰ๅ…ฅๅ™จๅ‡ฝๆ•ธๆ‹‹ๅ‡บ็•ฐๅธธ๏ผŒๅฐๆ‡‰็š„ๅฟซๅ–้ …็›ฎๆœƒ่ขซ่‡ชๅ‹•็งป้™ค
275
- - **ไฟๅญ˜ๅ™จๅคฑๆ•—**๏ผšๅฆ‚ๆžœไฟๅญ˜ๅ™จๅ‡ฝๆ•ธๅคฑๆ•—๏ผŒๅฟซๅ–้ …็›ฎไนŸๆœƒ่ขซ็งป้™ค๏ผŒ็ขบไฟ่ณ‡ๆ–™ไธ€่‡ดๆ€ง
276
- - **ๆ“ไฝœ้ˆ้Œฏ่ชค**๏ผšPUT ๆ“ไฝœๆœƒๅฟฝ็•ฅๅ‰ไธ€ๅ€‹ๆ“ไฝœ็š„้Œฏ่ชค๏ผŒๅ…่จฑๆ–ฐๆ“ไฝœ็นผ็บŒ้€ฒ่กŒ
277
- - **้Œฏ่ชคๆ—ฅ่ชŒ**๏ผšๆ‰€ๆœ‰้Œฏ่ชค้ƒฝๆœƒ่‡ชๅ‹•่จ˜้Œ„ๅˆฐๆŽงๅˆถๅฐ๏ผŒไพฟๆ–ผ้™ค้Œฏ
145
+ ๆญคๅฅ—ไปถไปฅ TypeScript ๆ’ฐๅฏซไธฆๅ…งๅปบๅž‹ๅˆฅๅฎฃๅ‘Šๆช”๏ผŒ็„ก้œ€ๅฆๅค–ๅฎ‰่ฃ `@types` ๅฅ—ไปถใ€‚
278
146
 
279
147
  ## ๆŽˆๆฌŠ
280
148
 
@@ -284,6 +152,3 @@ MIT
284
152
 
285
153
  ๆญก่ฟŽๅœจ [GitHub](https://github.com/wfp99/async-lru-cache) ไธŠๆๅ‡บๅ•้กŒๅ’Œ็™ผ้€ Pull Requestใ€‚
286
154
 
287
- ## ไฝœ่€…
288
-
289
- Wang Feng Ping
package/dist/index.d.ts CHANGED
@@ -5,10 +5,31 @@
5
5
  *
6
6
  * @module AsyncLRUCache
7
7
  */
8
+ /**
9
+ * Identifies which internal operation produced an error passed to `AsyncLRUCacheOption.onError`.
10
+ * - `loader`: The loader function passed to `get()` rejected.
11
+ * - `saver`: The saver function passed to `put()` rejected.
12
+ * - `put-chain`: A previous (already-settled) operation for the same key failed; this is a
13
+ * recoverable warning, since the new `put()` operation is still allowed to proceed.
14
+ */
15
+ export type AsyncLRUCacheErrorSource = 'loader' | 'saver' | 'put-chain';
16
+ /**
17
+ * Context information supplied alongside an error to `AsyncLRUCacheOption.onError`.
18
+ */
19
+ export interface AsyncLRUCacheErrorContext<K> {
20
+ /**
21
+ * The cache key associated with the failed operation.
22
+ */
23
+ key: K;
24
+ /**
25
+ * Which internal operation produced the error.
26
+ */
27
+ source: AsyncLRUCacheErrorSource;
28
+ }
8
29
  /**
9
30
  * Configuration options for the AsyncLRUCache.
10
31
  */
11
- export interface AsyncLRUCacheOption {
32
+ export interface AsyncLRUCacheOption<K = unknown> {
12
33
  /**
13
34
  * Maximum capacity of the cache, this is the maximum number of items allowed.
14
35
  * Must not be less than 10.
@@ -26,6 +47,14 @@ export interface AsyncLRUCacheOption {
26
47
  * Setting this enables periodic background cleanup.
27
48
  */
28
49
  cleanupIntervalMs?: number;
50
+ /**
51
+ * Optional callback invoked whenever a loader/saver operation fails.
52
+ * When provided, this callback *replaces* the built-in `console.error`/`console.warn`
53
+ * logging, so you can forward errors to your own logger or monitoring system.
54
+ * If not provided, errors are logged to the console as before (default, backward-compatible behavior).
55
+ * This callback must not throw; any error thrown from it is ignored.
56
+ */
57
+ onError?: (error: unknown, context: AsyncLRUCacheErrorContext<K>) => void;
29
58
  }
30
59
  /**
31
60
  * Asynchronous LRU Cache class.
@@ -60,12 +89,16 @@ export declare class AsyncLRUCache<K, V> {
60
89
  * Timer ID for periodic cleanup of expired entries.
61
90
  */
62
91
  private cleanupTimer?;
92
+ /**
93
+ * Optional user-supplied error callback. When absent, errors are logged to the console.
94
+ */
95
+ private readonly onError?;
63
96
  /**
64
97
  * Creates an instance of AsyncLRUCache with the specified options.
65
98
  * @param option - The configuration options for the cache.
66
99
  * @throws {Error} Throws an error if the provided capacity is less than 10.
67
100
  */
68
- constructor(option: AsyncLRUCacheOption);
101
+ constructor(option: AsyncLRUCacheOption<K>);
69
102
  /**
70
103
  * Cleans up expired entries from the cache.
71
104
  * This method removes all nodes that have exceeded their TTL.
@@ -100,6 +133,15 @@ export declare class AsyncLRUCache<K, V> {
100
133
  * @param node - The node to add to the head of the list.
101
134
  */
102
135
  private _addToHead;
136
+ /**
137
+ * Reports an error that occurred during a loader/saver operation.
138
+ * Delegates to the user-supplied `onError` callback if one was provided in the
139
+ * constructor options; otherwise falls back to logging via `console.error`/`console.warn`.
140
+ * @param error - The error that was thrown/rejected.
141
+ * @param key - The cache key associated with the failed operation.
142
+ * @param source - Which internal operation produced the error.
143
+ */
144
+ private _reportError;
103
145
  /**
104
146
  * Retrieves data from the cache. If it does not exist, uses the loader to load it.
105
147
  * Automatically merges concurrent requests for the same key.
@@ -109,6 +151,25 @@ export declare class AsyncLRUCache<K, V> {
109
151
  * @returns A Promise that resolves to the required data.
110
152
  */
111
153
  get(key: K, loader: () => Promise<V>, ttlMs?: number): Promise<V>;
154
+ /**
155
+ * Retrieves data from the cache without loading it on a miss.
156
+ * On a cache hit, this behaves like the loader-based overload: the entry is moved to
157
+ * the head of the LRU list, same as a normal `get()`/`has()` hit.
158
+ * On a cache miss (including an expired entry), it resolves to `undefined` without
159
+ * adding anything to the cache, so it does not affect the LRU order.
160
+ * @param key - Cache key.
161
+ * @returns A Promise that resolves to the cached value, or `undefined` on a miss.
162
+ */
163
+ get(key: K): Promise<V | undefined>;
164
+ /**
165
+ * Returns the cached value for a key without triggering a loader and without
166
+ * affecting the LRU order, regardless of whether the key is a hit or a miss.
167
+ * If the entry has expired, it is evicted as a side effect of this check, the same
168
+ * way `has()` behaves; this does not count as an LRU-order-affecting access.
169
+ * @param key - Cache key.
170
+ * @returns The cached value's Promise if the key is present and not expired, otherwise `undefined`.
171
+ */
172
+ peek(key: K): Promise<V> | undefined;
112
173
  /**
113
174
  * Puts a value into the cache, and optionally executes a saver function to persist it.
114
175
  * This method serializes multiple put operations for the same key, ensuring they execute in order.
@@ -145,8 +206,15 @@ export declare class AsyncLRUCache<K, V> {
145
206
  size(): number;
146
207
  /**
147
208
  * Checks if a key exists in the cache and is not expired.
209
+ *
210
+ * Note: this method is **not** side-effect-free. If the entry has expired, it is
211
+ * immediately evicted from the cache as part of this check (same as what would
212
+ * happen on the next `get()`/cleanup pass). It does **not** affect LRU order for
213
+ * entries that are still valid (unlike `get()`, calling `has()` does not move the
214
+ * entry to the head of the list).
148
215
  * @param key - The key to check.
149
216
  * @returns True if the key exists and is not expired, false otherwise.
150
217
  */
151
218
  has(key: K): boolean;
152
219
  }
220
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA8DH;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAE3C;;OAEG;IACH,GAAG,EAAE,CAAC,CAAC;IAEP;;OAEG;IACH,MAAM,EAAE,wBAAwB,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,GAAG,OAAO;IAE/C;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CAC1E;AAED;;;;;;;GAOG;AACH,qBAAa,aAAa,CAAC,CAAC,EAAE,CAAC;IAE9B;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAElC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IAEvC;;OAEG;IACH,OAAO,CAAC,OAAO,CAA4B;IAE3C;;OAEG;IACH,OAAO,CAAC,IAAI,CAA2B;IAEvC;;OAEG;IACH,OAAO,CAAC,IAAI,CAA2B;IAEvC;;OAEG;IACH,OAAO,CAAC,YAAY,CAAC,CAAiB;IAEtC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAkE;IAE3F;;;;OAIG;gBACS,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAkB1C;;;OAGG;IACH,OAAO,CAAC,eAAe;IAyBvB;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAW9B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAStB;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAQnB;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAanB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAYlB;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IA6BpB;;;;;;;OAOG;IACI,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAExE;;;;;;;;OAQG;IACI,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAiD1C;;;;;;;OAOG;IACI,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS;IAgB3C;;;;;;;;OAQG;IACI,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IA4DrG;;;OAGG;IACI,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI;IAW/B;;OAEG;IACI,KAAK,IAAI,IAAI;IA2BpB;;;OAGG;IACI,OAAO,IAAI,IAAI;IAatB;;;OAGG;IACI,cAAc,IAAI,IAAI;IAK7B;;;OAGG;IACI,IAAI,IAAI,MAAM;IAKrB;;;;;;;;;;OAUG;IACI,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;CAe3B"}
package/dist/index.js CHANGED
@@ -79,6 +79,7 @@ class AsyncLRUCache {
79
79
  throw new Error("Capacity must be at least 10.");
80
80
  this.capacity = option.capacity;
81
81
  this.defaultTtlMs = option.defaultTtlMs;
82
+ this.onError = option.onError;
82
83
  // Setup periodic cleanup if specified
83
84
  if (option.cleanupIntervalMs && option.cleanupIntervalMs > 0) {
84
85
  this.cleanupTimer = setInterval(() => {
@@ -171,33 +172,60 @@ class AsyncLRUCache {
171
172
  this.tail = node;
172
173
  }
173
174
  /**
174
- * Retrieves data from the cache. If it does not exist, uses the loader to load it.
175
- * Automatically merges concurrent requests for the same key.
176
- * @param key - Cache key.
177
- * @param loader - Asynchronous loader function to execute on cache miss.
178
- * @param ttlMs - Optional TTL override for this entry in milliseconds.
179
- * @returns A Promise that resolves to the required data.
175
+ * Reports an error that occurred during a loader/saver operation.
176
+ * Delegates to the user-supplied `onError` callback if one was provided in the
177
+ * constructor options; otherwise falls back to logging via `console.error`/`console.warn`.
178
+ * @param error - The error that was thrown/rejected.
179
+ * @param key - The cache key associated with the failed operation.
180
+ * @param source - Which internal operation produced the error.
180
181
  */
182
+ _reportError(error, key, source) {
183
+ if (this.onError) {
184
+ try {
185
+ this.onError(error, { key, source });
186
+ }
187
+ catch {
188
+ // Swallow errors thrown by the user-supplied callback; it must not break cache operations.
189
+ }
190
+ return;
191
+ }
192
+ switch (source) {
193
+ case 'loader':
194
+ console.error(`[AsyncLRUCache ERROR] Loader for key ${key} failed. Removing from cache.`, error);
195
+ break;
196
+ case 'saver':
197
+ console.error(`[AsyncLRUCache ERROR] Saver operation for key ${key} failed. Removing from cache.`, error);
198
+ break;
199
+ case 'put-chain':
200
+ console.warn(`[AsyncLRUCache WARN] Previous operation for key ${key} failed. Chaining new PUT operation.`, error?.message ?? error);
201
+ break;
202
+ }
203
+ }
181
204
  get(key, loader, ttlMs) {
182
205
  const node = this.dataMap.get(key);
183
206
  if (node) {
184
207
  // Check if the node has expired
185
208
  if (this._checkAndRemoveExpired(key, node)) {
186
- // Node was expired and removed, proceed to load fresh data
209
+ // Node was expired and removed, proceed to load fresh data (if a loader is provided)
187
210
  }
188
211
  else {
189
212
  this._moveToHead(node);
190
213
  return node.value;
191
214
  }
192
215
  }
216
+ if (!loader) {
217
+ // No loader was provided and there is no valid cached value.
218
+ // Resolve to undefined without touching the cache or its LRU order.
219
+ return Promise.resolve(undefined);
220
+ }
193
221
  const loadingPromise = loader();
194
- const effectiveTtlMs = ttlMs !== null && ttlMs !== void 0 ? ttlMs : this.defaultTtlMs;
222
+ const effectiveTtlMs = ttlMs ?? this.defaultTtlMs;
195
223
  const newNode = new Node(key, loadingPromise, effectiveTtlMs);
196
224
  this.dataMap.set(key, newNode);
197
225
  this._addToHead(newNode);
198
226
  this._evictIfNeeded();
199
227
  loadingPromise.catch(err => {
200
- console.error(`[AsyncLRUCache ERROR] Loader for key ${key} failed. Removing from cache.`, err);
228
+ this._reportError(err, key, 'loader');
201
229
  if (this.dataMap.get(key) === newNode) {
202
230
  this.dataMap.delete(key);
203
231
  this._removeNode(newNode);
@@ -205,6 +233,24 @@ class AsyncLRUCache {
205
233
  });
206
234
  return loadingPromise;
207
235
  }
236
+ /**
237
+ * Returns the cached value for a key without triggering a loader and without
238
+ * affecting the LRU order, regardless of whether the key is a hit or a miss.
239
+ * If the entry has expired, it is evicted as a side effect of this check, the same
240
+ * way `has()` behaves; this does not count as an LRU-order-affecting access.
241
+ * @param key - Cache key.
242
+ * @returns The cached value's Promise if the key is present and not expired, otherwise `undefined`.
243
+ */
244
+ peek(key) {
245
+ const node = this.dataMap.get(key);
246
+ if (!node) {
247
+ return undefined;
248
+ }
249
+ if (this._checkAndRemoveExpired(key, node)) {
250
+ return undefined;
251
+ }
252
+ return node.value;
253
+ }
208
254
  /**
209
255
  * Puts a value into the cache, and optionally executes a saver function to persist it.
210
256
  * This method serializes multiple put operations for the same key, ensuring they execute in order.
@@ -218,7 +264,7 @@ class AsyncLRUCache {
218
264
  const lastPromise = this.dataMap.has(key) ? this.dataMap.get(key).value : Promise.resolve(undefined);
219
265
  const saverPromise = lastPromise.catch((err) => {
220
266
  // Ignore previous operation errors, so the new operation can proceed.
221
- console.warn(`[AsyncLRUCache WARN] Previous operation for key ${key} failed. Chaining new PUT operation.`, err.message);
267
+ this._reportError(err, key, 'put-chain');
222
268
  })
223
269
  .then(() => {
224
270
  // After the previous operation (success or failure) completes, execute this saver.
@@ -230,7 +276,7 @@ class AsyncLRUCache {
230
276
  return value;
231
277
  });
232
278
  let node = this.dataMap.get(key);
233
- const effectiveTtlMs = ttlMs !== null && ttlMs !== void 0 ? ttlMs : this.defaultTtlMs;
279
+ const effectiveTtlMs = ttlMs ?? this.defaultTtlMs;
234
280
  if (!node) {
235
281
  node = new Node(key, saverPromise, effectiveTtlMs);
236
282
  this.dataMap.set(key, node);
@@ -249,9 +295,8 @@ class AsyncLRUCache {
249
295
  this._moveToHead(node);
250
296
  }
251
297
  saverPromise.catch(err => {
252
- var _a;
253
- console.error(`[AsyncLRUCache ERROR] Saver operation for key ${key} failed. Removing from cache.`, err);
254
- if (((_a = this.dataMap.get(key)) === null || _a === void 0 ? void 0 : _a.value) === saverPromise) {
298
+ this._reportError(err, key, 'saver');
299
+ if (this.dataMap.get(key)?.value === saverPromise) {
255
300
  this.dataMap.delete(key);
256
301
  this._removeNode(node);
257
302
  }
@@ -320,6 +365,12 @@ class AsyncLRUCache {
320
365
  }
321
366
  /**
322
367
  * Checks if a key exists in the cache and is not expired.
368
+ *
369
+ * Note: this method is **not** side-effect-free. If the entry has expired, it is
370
+ * immediately evicted from the cache as part of this check (same as what would
371
+ * happen on the next `get()`/cleanup pass). It does **not** affect LRU order for
372
+ * entries that are still valid (unlike `get()`, calling `has()` does not move the
373
+ * entry to the head of the list).
323
374
  * @param key - The key to check.
324
375
  * @returns True if the key exists and is not expired, false otherwise.
325
376
  */
@@ -335,3 +386,4 @@ class AsyncLRUCache {
335
386
  }
336
387
  }
337
388
  exports.AsyncLRUCache = AsyncLRUCache;
389
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wfp99/async-lru-cache",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "A simple memory cache using the LRU algorithm and supporting asynchronous data access.",
5
5
  "keywords": [
6
6
  "cache",
@@ -14,6 +14,13 @@
14
14
  "type": "commonjs",
15
15
  "main": "dist/index.js",
16
16
  "types": "dist/index.d.ts",
17
+ "sideEffects": false,
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "require": "./dist/index.js"
22
+ }
23
+ },
17
24
  "files": [
18
25
  "dist/",
19
26
  "README.md",
@@ -34,11 +41,13 @@
34
41
  "scripts": {
35
42
  "build": "tsc",
36
43
  "clean": "rm -rf dist",
37
- "prepublishOnly": "npm run clean && npm run build",
38
- "test": "echo \"Error: no test specified\" && exit 1"
44
+ "prepublishOnly": "npm run clean && npm run build && npm test",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest"
39
47
  },
40
48
  "devDependencies": {
41
49
  "@types/node": "^24.0.7",
42
- "typescript": "^5.8.3"
50
+ "typescript": "^5.8.3",
51
+ "vitest": "^3.2.4"
43
52
  }
44
53
  }