source: trip-planner-front/node_modules/mem/readme.md@ 6a3a178

Last change on this file since 6a3a178 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 8.0 KB
Line 
1# mem
2
3> [Memoize](https://en.wikipedia.org/wiki/Memoization) functions - An optimization used to speed up consecutive function calls by caching the result of calls with identical input
4
5Memory is automatically released when an item expires or the cache is cleared.
6
7By default, **only the first argument is considered** and it only works with [primitives](https://developer.mozilla.org/en-US/docs/Glossary/Primitive). If you need to cache multiple arguments or cache `object`s *by value*, have a look at alternative [caching strategies](#caching-strategy) below.
8
9## Install
10
11```
12$ npm install mem
13```
14
15## Usage
16
17```js
18const mem = require('mem');
19
20let i = 0;
21const counter = () => ++i;
22const memoized = mem(counter);
23
24memoized('foo');
25//=> 1
26
27// Cached as it's the same argument
28memoized('foo');
29//=> 1
30
31// Not cached anymore as the argument changed
32memoized('bar');
33//=> 2
34
35memoized('bar');
36//=> 2
37
38// Only the first argument is considered by default
39memoized('bar', 'foo');
40//=> 2
41```
42
43##### Works fine with promise returning functions
44
45```js
46const mem = require('mem');
47
48let i = 0;
49const counter = async () => ++i;
50const memoized = mem(counter);
51
52(async () => {
53 console.log(await memoized());
54 //=> 1
55
56 // The return value didn't increase as it's cached
57 console.log(await memoized());
58 //=> 1
59})();
60```
61
62```js
63const mem = require('mem');
64const got = require('got');
65const delay = require('delay');
66
67const memGot = mem(got, {maxAge: 1000});
68
69(async () => {
70 await memGot('https://sindresorhus.com');
71
72 // This call is cached
73 await memGot('https://sindresorhus.com');
74
75 await delay(2000);
76
77 // This call is not cached as the cache has expired
78 await memGot('https://sindresorhus.com');
79})();
80```
81
82### Caching strategy
83
84By default, only the first argument is compared via exact equality (`===`) to determine whether a call is identical.
85
86```js
87const power = mem((a, b) => Math.power(a, b));
88
89power(2, 2); // => 4, stored in cache with the key 2 (number)
90power(2, 3); // => 4, retrieved from cache at key 2 (number), it's wrong
91```
92
93You will have to use the `cache` and `cacheKey` options appropriate to your function. In this specific case, the following could work:
94
95```js
96const power = mem((a, b) => Math.power(a, b), {
97 cacheKey: arguments_ => arguments_.join(',')
98});
99
100power(2, 2); // => 4, stored in cache with the key '2,2' (both arguments as one string)
101power(2, 3); // => 8, stored in cache with the key '2,3'
102```
103
104More advanced examples follow.
105
106#### Example: Options-like argument
107
108If your function accepts an object, it won't be memoized out of the box:
109
110```js
111const heavyMemoizedOperation = mem(heavyOperation);
112
113heavyMemoizedOperation({full: true}); // Stored in cache with the object as key
114heavyMemoizedOperation({full: true}); // Stored in cache with the object as key, again
115// The objects look the same but for JS they're two different objects
116```
117
118You might want to serialize or hash them, for example using `JSON.stringify` or something like [serialize-javascript](https://github.com/yahoo/serialize-javascript), which can also serialize `RegExp`, `Date` and so on.
119
120```js
121const heavyMemoizedOperation = mem(heavyOperation, {cacheKey: JSON.stringify});
122
123heavyMemoizedOperation({full: true}); // Stored in cache with the key '[{"full":true}]' (string)
124heavyMemoizedOperation({full: true}); // Retrieved from cache
125```
126
127The same solution also works if it accepts multiple serializable objects:
128
129```js
130const heavyMemoizedOperation = mem(heavyOperation, {cacheKey: JSON.stringify});
131
132heavyMemoizedOperation('hello', {full: true}); // Stored in cache with the key '["hello",{"full":true}]' (string)
133heavyMemoizedOperation('hello', {full: true}); // Retrieved from cache
134```
135
136#### Example: Multiple non-serializable arguments
137
138If your function accepts multiple arguments that aren't supported by `JSON.stringify` (e.g. DOM elements and functions), you can instead extend the initial exact equality (`===`) to work on multiple arguments using [`many-keys-map`](https://github.com/fregante/many-keys-map):
139
140```js
141const ManyKeysMap = require('many-keys-map');
142
143const addListener = (emitter, eventName, listener) => emitter.on(eventName, listener);
144
145const addOneListener = mem(addListener, {
146 cacheKey: arguments_ => arguments_, // Use *all* the arguments as key
147 cache: new ManyKeysMap() // Correctly handles all the arguments for exact equality
148});
149
150addOneListener(header, 'click', console.log); // `addListener` is run, and it's cached with the `arguments` array as key
151addOneListener(header, 'click', console.log); // `addListener` is not run again
152addOneListener(mainContent, 'load', console.log); // `addListener` is run, and it's cached with the `arguments` array as key
153```
154
155Better yet, if your function’s arguments are compatible with `WeakMap`, you should use [`deep-weak-map`](https://github.com/futpib/deep-weak-map) instead of `many-keys-map`. This will help avoid memory leaks.
156
157## API
158
159### mem(fn, options?)
160
161#### fn
162
163Type: `Function`
164
165Function to be memoized.
166
167#### options
168
169Type: `object`
170
171##### maxAge
172
173Type: `number`\
174Default: `Infinity`
175
176Milliseconds until the cache expires.
177
178##### cacheKey
179
180Type: `Function`\
181Default: `arguments_ => arguments_[0]`\
182Example: `arguments_ => JSON.stringify(arguments_)`
183
184Determines the cache key for storing the result based on the function arguments. By default, **only the first argument is considered**.
185
186A `cacheKey` function can return any type supported by `Map` (or whatever structure you use in the `cache` option).
187
188Refer to the [caching strategies](#caching-strategy) section for more information.
189
190##### cache
191
192Type: `object`\
193Default: `new Map()`
194
195Use a different cache storage. Must implement the following methods: `.has(key)`, `.get(key)`, `.set(key, value)`, `.delete(key)`, and optionally `.clear()`. You could for example use a `WeakMap` instead or [`quick-lru`](https://github.com/sindresorhus/quick-lru) for a LRU cache.
196
197Refer to the [caching strategies](#caching-strategy) section for more information.
198
199### mem.decorator(options)
200
201Returns a [decorator](https://github.com/tc39/proposal-decorators) to memoize class methods or static class methods.
202
203Notes:
204
205- Only class methods and getters/setters can be memoized, not regular functions (they aren't part of the proposal);
206- Only [TypeScript’s decorators](https://www.typescriptlang.org/docs/handbook/decorators.html#parameter-decorators) are supported, not [Babel’s](https://babeljs.io/docs/en/babel-plugin-proposal-decorators), which use a different version of the proposal;
207- Being an experimental feature, they need to be enabled with `--experimentalDecorators`; follow TypeScript’s docs.
208
209#### options
210
211Type: `object`
212
213Same as options for `mem()`.
214
215```ts
216import mem = require('mem');
217
218class Example {
219 index = 0
220
221 @mem.decorator()
222 counter() {
223 return ++this.index;
224 }
225}
226
227class ExampleWithOptions {
228 index = 0
229
230 @mem.decorator({maxAge: 1000})
231 counter() {
232 return ++this.index;
233 }
234}
235```
236
237### mem.clear(fn)
238
239Clear all cached data of a memoized function.
240
241#### fn
242
243Type: `Function`
244
245Memoized function.
246
247## Tips
248
249### Cache statistics
250
251If you want to know how many times your cache had a hit or a miss, you can make use of [stats-map](https://github.com/SamVerschueren/stats-map) as a replacement for the default cache.
252
253#### Example
254
255```js
256const mem = require('mem');
257const StatsMap = require('stats-map');
258const got = require('got');
259
260const cache = new StatsMap();
261const memGot = mem(got, {cache});
262
263(async () => {
264 await memGot('https://sindresorhus.com');
265 await memGot('https://sindresorhus.com');
266 await memGot('https://sindresorhus.com');
267
268 console.log(cache.stats);
269 //=> {hits: 2, misses: 1}
270})();
271```
272
273## Related
274
275- [p-memoize](https://github.com/sindresorhus/p-memoize) - Memoize promise-returning & async functions
276
277---
278
279<div align="center">
280 <b>
281 <a href="https://tidelift.com/subscription/pkg/npm-mem?utm_source=npm-mem&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
282 </b>
283 <br>
284 <sub>
285 Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
286 </sub>
287</div>
Note: See TracBrowser for help on using the repository browser.