1 | # p-memoize
|
2 |
|
3 | > [Memoize](https://en.wikipedia.org/wiki/Memoization) promise-returning & async functions
|
4 |
|
5 | Useful for speeding up consecutive function calls by caching the result of calls with identical input.
|
6 |
|
7 |
|
8 |
|
9 | By default, **only the memoized function's first argument is considered** via strict equality comparison. If you need to cache multiple arguments or cache `object`s *by value*, have a look at alternative [caching strategies](#caching-strategy) below.
|
10 |
|
11 | This package is similar to [mem](https://github.com/sindresorhus/mem) but with async-specific enhancements; in particular, it allows for asynchronous caches and does not cache rejected promises.
|
12 |
|
13 | ## Install
|
14 |
|
15 | ```sh
|
16 | npm install p-memoize
|
17 | ```
|
18 |
|
19 | ## Usage
|
20 |
|
21 | ```js
|
22 | import pMemoize from 'p-memoize';
|
23 | import got from 'got';
|
24 |
|
25 | const memoizedGot = pMemoize(got);
|
26 |
|
27 | await memoizedGot('https://sindresorhus.com');
|
28 |
|
29 | // This call is cached
|
30 | await memoizedGot('https://sindresorhus.com');
|
31 | ```
|
32 |
|
33 | ### Caching strategy
|
34 |
|
35 | Similar to the [caching strategy for `mem`](https://github.com/sindresorhus/mem#options) with the following exceptions:
|
36 |
|
37 | - Promises returned from a memoized function are locally cached until resolving, when their value is added to `cache`. Special properties assigned to a returned promise will not be kept after resolution and every promise may need to resolve with a serializable object if caching results in a database.
|
38 | - `.get()`, `.has()` and `.set()` methods on `cache` can run asynchronously by returning a promise.
|
39 | - Instead of `.set()` being provided an object with the properties `value` and `maxAge`, it will only be provided `value` as the first argument. If you want to implement time-based expiry, consider [doing so in `cache`](#time-based-cache-expiration).
|
40 |
|
41 | ## API
|
42 |
|
43 | ### pMemoize(fn, options?)
|
44 |
|
45 | Returns a memoized version of the given function.
|
46 |
|
47 | #### fn
|
48 |
|
49 | Type: `Function`
|
50 |
|
51 | Promise-returning or async function to be memoized.
|
52 |
|
53 | #### options
|
54 |
|
55 | Type: `object`
|
56 |
|
57 | ##### cacheKey
|
58 |
|
59 | Type: `Function`\
|
60 | Default: `arguments_ => arguments_[0]`\
|
61 | Example: `arguments_ => JSON.stringify(arguments_)`
|
62 |
|
63 | Determines the cache key for storing the result based on the function arguments. By default, **only the first argument is considered**.
|
64 |
|
65 | A `cacheKey` function can return any type supported by `Map` (or whatever structure you use in the `cache` option).
|
66 |
|
67 | See the [caching strategy](#caching-strategy) section for more information.
|
68 |
|
69 | ##### cache
|
70 |
|
71 | Type: `object | false`\
|
72 | Default: `new Map()`
|
73 |
|
74 | Use 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. To disable caching so that only concurrent executions resolve with the same value, pass `false`.
|
75 |
|
76 | See the [caching strategy](https://github.com/sindresorhus/mem#caching-strategy) section in the `mem` package for more information.
|
77 |
|
78 | ### pMemoizeDecorator(options)
|
79 |
|
80 | Returns a [decorator](https://github.com/tc39/proposal-decorators) to memoize class methods or static class methods.
|
81 |
|
82 | Notes:
|
83 |
|
84 | - Only class methods and getters/setters can be memoized, not regular functions (they aren't part of the proposal);
|
85 | - 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;
|
86 | - Being an experimental feature, they need to be enabled with `--experimentalDecorators`; follow TypeScript’s docs.
|
87 |
|
88 | #### options
|
89 |
|
90 | Type: `object`
|
91 |
|
92 | Same as options for `pMemoize()`.
|
93 |
|
94 | ```ts
|
95 | import {pMemoizeDecorator} from 'p-memoize';
|
96 |
|
97 | class Example {
|
98 | index = 0
|
99 |
|
100 | @pMemoizeDecorator()
|
101 | async counter() {
|
102 | return ++this.index;
|
103 | }
|
104 | }
|
105 |
|
106 | class ExampleWithOptions {
|
107 | index = 0
|
108 |
|
109 | @pMemoizeDecorator()
|
110 | async counter() {
|
111 | return ++this.index;
|
112 | }
|
113 | }
|
114 | ```
|
115 |
|
116 | ### pMemoizeClear(memoized)
|
117 |
|
118 | Clear all cached data of a memoized function.
|
119 |
|
120 | It will throw when given a non-memoized function.
|
121 |
|
122 | ## Tips
|
123 |
|
124 | ### Time-based cache expiration
|
125 |
|
126 | ```js
|
127 | import pMemoize from 'p-memoize';
|
128 | import ExpiryMap from 'expiry-map';
|
129 | import got from 'got';
|
130 |
|
131 | const cache = new ExpiryMap(10000); // Cached values expire after 10 seconds
|
132 |
|
133 | const memoizedGot = pMemoize(got, {cache});
|
134 | ```
|
135 |
|
136 | ### Caching promise rejections
|
137 |
|
138 | ```js
|
139 | import pMemoize from 'p-memoize';
|
140 | import pReflect from 'p-reflect';
|
141 |
|
142 | const memoizedGot = pMemoize(async (url, options) => pReflect(got(url, options)));
|
143 |
|
144 | await memoizedGot('https://example.com');
|
145 | // {isFulfilled: true, isRejected: false, value: '...'}
|
146 | ```
|
147 |
|
148 | ## Related
|
149 |
|
150 | - [p-debounce](https://github.com/sindresorhus/p-debounce) - Debounce promise-returning & async functions
|
151 | - [p-throttle](https://github.com/sindresorhus/p-throttle) - Throttle promise-returning & async functions
|
152 | - [More…](https://github.com/sindresorhus/promise-fun)
|