@naturalcycles/js-lib 14.84.2 → 14.85.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/dist/decorators/asyncMemo.decorator.d.ts +32 -9
- package/dist/decorators/asyncMemo.decorator.js +26 -21
- package/dist/decorators/decorator.util.d.ts +1 -1
- package/dist/decorators/decorator.util.js +2 -2
- package/dist/decorators/logMethod.decorator.d.ts +6 -4
- package/dist/decorators/logMethod.decorator.js +3 -3
- package/dist/decorators/memo.decorator.d.ts +27 -26
- package/dist/decorators/memo.decorator.js +25 -45
- package/dist/decorators/memo.util.d.ts +6 -6
- package/dist/decorators/memoFn.d.ts +5 -0
- package/dist/decorators/memoFn.js +21 -40
- package/dist/decorators/memoFnAsync.d.ts +10 -0
- package/dist/decorators/memoFnAsync.js +66 -0
- package/dist/decorators/memoSimple.decorator.d.ts +1 -1
- package/dist/decorators/memoSimple.decorator.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist-esm/decorators/asyncMemo.decorator.js +27 -34
- package/dist-esm/decorators/decorator.util.js +2 -2
- package/dist-esm/decorators/logMethod.decorator.js +3 -3
- package/dist-esm/decorators/memo.decorator.js +25 -45
- package/dist-esm/decorators/memoFn.js +21 -40
- package/dist-esm/decorators/memoFnAsync.js +62 -0
- package/dist-esm/decorators/memoSimple.decorator.js +3 -3
- package/dist-esm/index.js +1 -0
- package/package.json +1 -1
- package/src/decorators/asyncMemo.decorator.ts +72 -48
- package/src/decorators/decorator.util.ts +2 -2
- package/src/decorators/logMethod.decorator.ts +16 -7
- package/src/decorators/memo.decorator.ts +46 -91
- package/src/decorators/memo.util.ts +7 -7
- package/src/decorators/memoFn.ts +21 -52
- package/src/decorators/memoFnAsync.ts +87 -0
- package/src/decorators/memoSimple.decorator.ts +4 -4
- package/src/index.ts +1 -0
|
@@ -16,14 +16,16 @@ export interface LogMethodOptions {
|
|
|
16
16
|
avg?: number
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Defaults to true.
|
|
20
|
+
* Set to false to skip logging method arguments
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
+
logArgs?: boolean
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
+
* Defaults to true.
|
|
26
|
+
* Set to false to skip logging result length when result is an array.
|
|
25
27
|
*/
|
|
26
|
-
|
|
28
|
+
logResultLength?: boolean
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Also log on method start.
|
|
@@ -74,12 +76,19 @@ export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
|
|
|
74
76
|
const originalFn = descriptor.value
|
|
75
77
|
const keyStr = String(key)
|
|
76
78
|
|
|
77
|
-
const {
|
|
79
|
+
const {
|
|
80
|
+
avg,
|
|
81
|
+
logArgs = true,
|
|
82
|
+
logStart,
|
|
83
|
+
logResult,
|
|
84
|
+
logResultLength = true,
|
|
85
|
+
logger = console,
|
|
86
|
+
} = opt
|
|
78
87
|
let { logResultFn } = opt
|
|
79
88
|
if (!logResultFn) {
|
|
80
89
|
if (logResult) {
|
|
81
90
|
logResultFn = r => ['result:', _stringifyAny(r)]
|
|
82
|
-
} else if (
|
|
91
|
+
} else if (logResultLength) {
|
|
83
92
|
logResultFn = r => (Array.isArray(r) ? [`result: ${r.length} items`] : [])
|
|
84
93
|
}
|
|
85
94
|
}
|
|
@@ -94,7 +103,7 @@ export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
|
|
|
94
103
|
// e.g `NameOfYourClass.methodName`
|
|
95
104
|
// or `NameOfYourClass(instanceId).methodName`
|
|
96
105
|
const methodSignature = _getMethodSignature(ctx, keyStr)
|
|
97
|
-
const argsStr = _getArgsSignature(args,
|
|
106
|
+
const argsStr = _getArgsSignature(args, logArgs)
|
|
98
107
|
const callSignature = `${methodSignature}(${argsStr}) #${++count}`
|
|
99
108
|
if (logStart) logger.log(`>> ${callSignature}`)
|
|
100
109
|
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
// Based on:
|
|
2
|
-
// https://github.com/mgechev/memo-decorator/blob/master/index.ts
|
|
3
|
-
// http://decodize.com/blog/2012/08/27/javascript-memoization-caching-results-for-better-performance/
|
|
4
|
-
// http://inlehmansterms.net/2015/03/01/javascript-memoization/
|
|
5
|
-
// https://community.risingstack.com/the-worlds-fastest-javascript-memoization-library/
|
|
6
|
-
|
|
7
1
|
import { CommonLogger } from '../log/commonLogger'
|
|
8
2
|
import { _since } from '../time/time.util'
|
|
9
3
|
import { AnyObject } from '../types'
|
|
@@ -12,51 +6,42 @@ import { jsonMemoSerializer, MapMemoCache, MemoCache } from './memo.util'
|
|
|
12
6
|
|
|
13
7
|
export interface MemoOptions {
|
|
14
8
|
/**
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Default to false
|
|
9
|
+
* Provide a custom implementation of MemoCache.
|
|
10
|
+
* Function that creates an instance of `MemoCache`.
|
|
11
|
+
* e.g LRUMemoCache from `@naturalcycles/nodejs-lib`
|
|
20
12
|
*/
|
|
21
|
-
|
|
13
|
+
cacheFactory?: () => MemoCache
|
|
22
14
|
|
|
23
15
|
/**
|
|
24
|
-
*
|
|
16
|
+
* Provide a custom implementation of CacheKey function.
|
|
25
17
|
*/
|
|
26
|
-
|
|
18
|
+
cacheKeyFn?: (args: any[]) => any
|
|
27
19
|
|
|
28
20
|
/**
|
|
29
|
-
*
|
|
21
|
+
* Defaults to false.
|
|
22
|
+
* Set to true to cache thrown errors.
|
|
30
23
|
*/
|
|
31
|
-
|
|
24
|
+
cacheErrors?: boolean
|
|
32
25
|
|
|
33
26
|
/**
|
|
34
|
-
*
|
|
35
|
-
* Function that creates an instance of `MemoCache`.
|
|
36
|
-
* e.g LRUMemoCache from `@naturalcycles/nodejs-lib`
|
|
27
|
+
* Default to false
|
|
37
28
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
logHit?: boolean
|
|
40
30
|
/**
|
|
41
|
-
*
|
|
31
|
+
* Default to false
|
|
42
32
|
*/
|
|
43
|
-
|
|
33
|
+
logMiss?: boolean
|
|
44
34
|
|
|
45
35
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* Default false.
|
|
36
|
+
* Defaults to true.
|
|
37
|
+
* Set to false to skip logging method arguments.
|
|
50
38
|
*/
|
|
51
|
-
|
|
39
|
+
logArgs?: boolean
|
|
52
40
|
|
|
53
41
|
/**
|
|
54
|
-
*
|
|
55
|
-
* Setting this to `true` will make the decorator to await the result.
|
|
56
|
-
*
|
|
57
|
-
* Default false.
|
|
42
|
+
* Default to `console`
|
|
58
43
|
*/
|
|
59
|
-
|
|
44
|
+
logger?: CommonLogger
|
|
60
45
|
}
|
|
61
46
|
|
|
62
47
|
/**
|
|
@@ -68,6 +53,15 @@ export interface MemoOptions {
|
|
|
68
53
|
* If you don't want it that way - you can use a static method, then there will be only one "instance".
|
|
69
54
|
*
|
|
70
55
|
* Supports dropping it's cache by calling .dropCache() method of decorated function (useful in unit testing).
|
|
56
|
+
*
|
|
57
|
+
* Doesn't support Async functions, use @_AsyncMemo instead!
|
|
58
|
+
* (or, it will simply return the [unresolved] Promise further, without awaiting it)
|
|
59
|
+
*
|
|
60
|
+
* Based on:
|
|
61
|
+
* https://github.com/mgechev/memo-decorator/blob/master/index.ts
|
|
62
|
+
* http://decodize.com/blog/2012/08/27/javascript-memoization-caching-results-for-better-performance/
|
|
63
|
+
* http://inlehmansterms.net/2015/03/01/javascript-memoization/
|
|
64
|
+
* https://community.risingstack.com/the-worlds-fastest-javascript-memoization-library/
|
|
71
65
|
*/
|
|
72
66
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
73
67
|
export const _Memo =
|
|
@@ -91,15 +85,13 @@ export const _Memo =
|
|
|
91
85
|
const {
|
|
92
86
|
logHit = false,
|
|
93
87
|
logMiss = false,
|
|
94
|
-
|
|
88
|
+
logArgs = true,
|
|
95
89
|
logger = console,
|
|
96
90
|
cacheFactory = () => new MapMemoCache(),
|
|
97
91
|
cacheKeyFn = jsonMemoSerializer,
|
|
98
|
-
|
|
99
|
-
noCacheResolved = false,
|
|
92
|
+
cacheErrors = false,
|
|
100
93
|
} = opt
|
|
101
94
|
|
|
102
|
-
const awaitPromise = Boolean(noCacheRejected || noCacheResolved)
|
|
103
95
|
const keyStr = String(key)
|
|
104
96
|
const methodSignature = _getTargetMethodSignature(target, keyStr)
|
|
105
97
|
|
|
@@ -113,74 +105,37 @@ export const _Memo =
|
|
|
113
105
|
} else if (cache.get(ctx)!.has(cacheKey)) {
|
|
114
106
|
if (logHit) {
|
|
115
107
|
logger.log(
|
|
116
|
-
`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args,
|
|
108
|
+
`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, logArgs)}) @_Memo hit`,
|
|
117
109
|
)
|
|
118
110
|
}
|
|
119
111
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (awaitPromise) {
|
|
123
|
-
return res instanceof Error ? Promise.reject(res) : Promise.resolve(res)
|
|
124
|
-
} else {
|
|
125
|
-
return res
|
|
126
|
-
}
|
|
112
|
+
return cache.get(ctx)!.get(cacheKey)
|
|
127
113
|
}
|
|
128
114
|
|
|
129
115
|
const started = Date.now()
|
|
116
|
+
let value: any
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
value = originalFn.apply(ctx, args)
|
|
130
120
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
noLogArgs,
|
|
142
|
-
)}) @_Memo miss resolved (${_since(started)})`,
|
|
143
|
-
)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (!noCacheResolved) {
|
|
147
|
-
cache.get(ctx)!.set(cacheKey, res)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return res
|
|
151
|
-
})
|
|
152
|
-
.catch(err => {
|
|
153
|
-
// console.log('REJECTED', err)
|
|
154
|
-
if (logMiss) {
|
|
155
|
-
logger.log(
|
|
156
|
-
`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(
|
|
157
|
-
args,
|
|
158
|
-
noLogArgs,
|
|
159
|
-
)}) @_Memo miss rejected (${_since(started)})`,
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (!noCacheRejected) {
|
|
164
|
-
// We put it to cache as raw Error, not Promise.reject(err)
|
|
165
|
-
// So, we'll need to check if it's instanceof Error to reject it or resolve
|
|
166
|
-
// Wrap as Error if it's not Error
|
|
167
|
-
cache.get(ctx)!.set(cacheKey, err instanceof Error ? err : new Error(err))
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
throw err
|
|
171
|
-
})
|
|
172
|
-
} else {
|
|
121
|
+
cache.get(ctx)!.set(cacheKey, value)
|
|
122
|
+
|
|
123
|
+
return value
|
|
124
|
+
} catch (err) {
|
|
125
|
+
if (cacheErrors) {
|
|
126
|
+
cache.get(ctx)!.set(cacheKey, err)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
throw err
|
|
130
|
+
} finally {
|
|
173
131
|
if (logMiss) {
|
|
174
132
|
logger.log(
|
|
175
133
|
`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(
|
|
176
134
|
args,
|
|
177
|
-
|
|
135
|
+
logArgs,
|
|
178
136
|
)}) @_Memo miss (${_since(started)})`,
|
|
179
137
|
)
|
|
180
138
|
}
|
|
181
|
-
|
|
182
|
-
cache.get(ctx)!.set(cacheKey, res)
|
|
183
|
-
return res
|
|
184
139
|
}
|
|
185
140
|
} as any
|
|
186
141
|
;(descriptor.value as any).dropCache = () => {
|
|
@@ -11,8 +11,8 @@ export const jsonMemoSerializer: MemoSerializer = args => {
|
|
|
11
11
|
|
|
12
12
|
export interface MemoCache<KEY = any, VALUE = any> {
|
|
13
13
|
has(k: KEY): boolean
|
|
14
|
-
get(k: KEY): VALUE | undefined
|
|
15
|
-
set(k: KEY, v: VALUE): void
|
|
14
|
+
get(k: KEY): VALUE | Error | undefined
|
|
15
|
+
set(k: KEY, v: VALUE | Error): void
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Clear is only called when `.dropCache()` is called.
|
|
@@ -29,8 +29,8 @@ export interface AsyncMemoCache<KEY = any, VALUE = any> {
|
|
|
29
29
|
* This also means that you CANNOT store `undefined` value in the Cache, as it'll be treated as a MISS.
|
|
30
30
|
* You CAN store `null` value instead, it will be treated as a HIT.
|
|
31
31
|
*/
|
|
32
|
-
get(k: KEY): Promisable<VALUE | undefined>
|
|
33
|
-
set(k: KEY, v: VALUE): Promisable<void>
|
|
32
|
+
get(k: KEY): Promisable<VALUE | Error | undefined>
|
|
33
|
+
set(k: KEY, v: VALUE | Error): Promisable<void>
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Clear is only called when `.dropCache()` is called.
|
|
@@ -88,17 +88,17 @@ export class ObjectMemoCache implements MemoCache {
|
|
|
88
88
|
export class MapMemoCache<KEY = any, VALUE = any>
|
|
89
89
|
implements MemoCache<KEY, VALUE>, AsyncMemoCache<KEY, VALUE>
|
|
90
90
|
{
|
|
91
|
-
private m = new Map<KEY, VALUE>()
|
|
91
|
+
private m = new Map<KEY, VALUE | Error>()
|
|
92
92
|
|
|
93
93
|
has(k: KEY): boolean {
|
|
94
94
|
return this.m.has(k)
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
get(k: KEY): VALUE | undefined {
|
|
97
|
+
get(k: KEY): VALUE | Error | undefined {
|
|
98
98
|
return this.m.get(k)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
set(k: KEY, v: VALUE): void {
|
|
101
|
+
set(k: KEY, v: VALUE | Error): void {
|
|
102
102
|
this.m.set(k, v)
|
|
103
103
|
}
|
|
104
104
|
|
package/src/decorators/memoFn.ts
CHANGED
|
@@ -7,6 +7,11 @@ export interface MemoizedFunction {
|
|
|
7
7
|
cache: MemoCache
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Only supports Sync functions.
|
|
12
|
+
* To support Async functions - use _memoFnAsync.
|
|
13
|
+
* Technically, you can use it with Async functions, but it'll return the Promise without awaiting it.
|
|
14
|
+
*/
|
|
10
15
|
export function _memoFn<T extends (...args: any[]) => any>(
|
|
11
16
|
fn: T,
|
|
12
17
|
opt: MemoOptions = {},
|
|
@@ -14,16 +19,14 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
14
19
|
const {
|
|
15
20
|
logHit = false,
|
|
16
21
|
logMiss = false,
|
|
17
|
-
|
|
22
|
+
logArgs = true,
|
|
18
23
|
logger = console,
|
|
19
|
-
|
|
20
|
-
noCacheResolved = false,
|
|
24
|
+
cacheErrors = false,
|
|
21
25
|
cacheFactory = () => new MapMemoCache(),
|
|
22
26
|
cacheKeyFn = jsonMemoSerializer,
|
|
23
27
|
} = opt
|
|
24
28
|
|
|
25
29
|
const cache = cacheFactory()
|
|
26
|
-
const awaitPromise = Boolean(noCacheRejected || noCacheResolved)
|
|
27
30
|
const fnName = fn.name
|
|
28
31
|
|
|
29
32
|
const memoizedFn = function (this: any, ...args: any[]): T {
|
|
@@ -32,68 +35,34 @@ export function _memoFn<T extends (...args: any[]) => any>(
|
|
|
32
35
|
|
|
33
36
|
if (cache.has(cacheKey)) {
|
|
34
37
|
if (logHit) {
|
|
35
|
-
logger.log(`${fnName}(${_getArgsSignature(args,
|
|
38
|
+
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFn hit`)
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (awaitPromise) {
|
|
41
|
-
return res instanceof Error ? (Promise.reject(res) as any) : Promise.resolve(res)
|
|
42
|
-
} else {
|
|
43
|
-
return res
|
|
44
|
-
}
|
|
41
|
+
return cache.get(cacheKey)
|
|
45
42
|
}
|
|
46
43
|
|
|
47
44
|
const started = Date.now()
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (awaitPromise) {
|
|
52
|
-
return (res as Promise<any>)
|
|
53
|
-
.then(res => {
|
|
54
|
-
// console.log('RESOLVED', res)
|
|
55
|
-
if (logMiss) {
|
|
56
|
-
logger.log(
|
|
57
|
-
`${fnName}(${_getArgsSignature(args, noLogArgs)}) memoFn miss resolved (${_since(
|
|
58
|
-
started,
|
|
59
|
-
)})`,
|
|
60
|
-
)
|
|
61
|
-
}
|
|
46
|
+
let value: any
|
|
62
47
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
48
|
+
try {
|
|
49
|
+
value = fn.apply(ctx, args)
|
|
66
50
|
|
|
67
|
-
|
|
68
|
-
})
|
|
69
|
-
.catch(err => {
|
|
70
|
-
// console.log('REJECTED', err)
|
|
71
|
-
if (logMiss) {
|
|
72
|
-
logger.log(
|
|
73
|
-
`${fnName}(${_getArgsSignature(args, noLogArgs)}) memoFn miss rejected (${_since(
|
|
74
|
-
started,
|
|
75
|
-
)})`,
|
|
76
|
-
)
|
|
77
|
-
}
|
|
51
|
+
cache.set(cacheKey, value)
|
|
78
52
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
53
|
+
return value
|
|
54
|
+
} catch (err) {
|
|
55
|
+
if (cacheErrors) {
|
|
56
|
+
cache.set(cacheKey, err)
|
|
57
|
+
}
|
|
85
58
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
} else {
|
|
59
|
+
throw err
|
|
60
|
+
} finally {
|
|
89
61
|
if (logMiss) {
|
|
90
62
|
logger.log(
|
|
91
|
-
`${fnName}(${_getArgsSignature(args,
|
|
63
|
+
`${fnName}(${_getArgsSignature(args, logArgs)}) memoFn miss (${_since(started)})`,
|
|
92
64
|
)
|
|
93
65
|
}
|
|
94
|
-
|
|
95
|
-
cache.set(cacheKey, res)
|
|
96
|
-
return res
|
|
97
66
|
}
|
|
98
67
|
}
|
|
99
68
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { _since } from '../time/time.util'
|
|
2
|
+
import { AsyncMemoOptions } from './asyncMemo.decorator'
|
|
3
|
+
import { _getArgsSignature } from './decorator.util'
|
|
4
|
+
import { AsyncMemoCache, jsonMemoSerializer, MapMemoCache } from './memo.util'
|
|
5
|
+
|
|
6
|
+
export interface MemoizedAsyncFunction {
|
|
7
|
+
cache: AsyncMemoCache
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Only supports Sync functions.
|
|
12
|
+
* To support Async functions - use _memoFnAsync
|
|
13
|
+
*/
|
|
14
|
+
export function _memoFnAsync<T extends (...args: any[]) => Promise<any>>(
|
|
15
|
+
fn: T,
|
|
16
|
+
opt: AsyncMemoOptions = {},
|
|
17
|
+
): T & MemoizedAsyncFunction {
|
|
18
|
+
const {
|
|
19
|
+
logHit = false,
|
|
20
|
+
logMiss = false,
|
|
21
|
+
logArgs = true,
|
|
22
|
+
logger = console,
|
|
23
|
+
cacheRejections = false,
|
|
24
|
+
cacheFactory = () => new MapMemoCache(),
|
|
25
|
+
cacheKeyFn = jsonMemoSerializer,
|
|
26
|
+
} = opt
|
|
27
|
+
|
|
28
|
+
const cache = cacheFactory()
|
|
29
|
+
const fnName = fn.name
|
|
30
|
+
|
|
31
|
+
const memoizedFn = async function (this: any, ...args: any[]): Promise<any> {
|
|
32
|
+
const ctx = this
|
|
33
|
+
const cacheKey = cacheKeyFn(args)
|
|
34
|
+
let value: any
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
value = await cache.get(cacheKey)
|
|
38
|
+
} catch (err) {
|
|
39
|
+
logger.error(err)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (value !== undefined) {
|
|
43
|
+
if (logHit) {
|
|
44
|
+
logger.log(`${fnName}(${_getArgsSignature(args, logArgs)}) memoFnAsync hit`)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const started = Date.now()
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
value = await fn.apply(ctx, args)
|
|
54
|
+
|
|
55
|
+
void (async () => {
|
|
56
|
+
try {
|
|
57
|
+
await cache.set(cacheKey, value)
|
|
58
|
+
} catch (err) {
|
|
59
|
+
logger.error(err)
|
|
60
|
+
}
|
|
61
|
+
})()
|
|
62
|
+
|
|
63
|
+
return value
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (cacheRejections) {
|
|
66
|
+
void (async () => {
|
|
67
|
+
try {
|
|
68
|
+
await cache.set(cacheKey, err)
|
|
69
|
+
} catch (err) {
|
|
70
|
+
logger.error(err)
|
|
71
|
+
}
|
|
72
|
+
})()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
throw err
|
|
76
|
+
} finally {
|
|
77
|
+
if (logMiss) {
|
|
78
|
+
logger.log(
|
|
79
|
+
`${fnName}(${_getArgsSignature(args, logArgs)}) memoFnAsync miss (${_since(started)})`,
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
Object.assign(memoizedFn, { cache })
|
|
86
|
+
return memoizedFn as T & MemoizedAsyncFunction
|
|
87
|
+
}
|
|
@@ -18,7 +18,7 @@ import { jsonMemoSerializer, MapMemoCache, MemoCache } from './memo.util'
|
|
|
18
18
|
export interface MemoOpts {
|
|
19
19
|
logHit?: boolean
|
|
20
20
|
logMiss?: boolean
|
|
21
|
-
|
|
21
|
+
logArgs?: boolean
|
|
22
22
|
logger?: CommonLogger
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -57,7 +57,7 @@ export const memoSimple =
|
|
|
57
57
|
*/
|
|
58
58
|
const cache: MemoCache = new MapMemoCache()
|
|
59
59
|
|
|
60
|
-
const { logHit, logMiss,
|
|
60
|
+
const { logHit, logMiss, logArgs = true, logger = console } = opt
|
|
61
61
|
const keyStr = String(key)
|
|
62
62
|
const methodSignature = _getTargetMethodSignature(target, keyStr)
|
|
63
63
|
|
|
@@ -67,7 +67,7 @@ export const memoSimple =
|
|
|
67
67
|
|
|
68
68
|
if (cache.has(cacheKey)) {
|
|
69
69
|
if (logHit) {
|
|
70
|
-
logger.log(`${methodSignature}(${_getArgsSignature(args,
|
|
70
|
+
logger.log(`${methodSignature}(${_getArgsSignature(args, logArgs)}) @memo hit`)
|
|
71
71
|
}
|
|
72
72
|
return cache.get(cacheKey)
|
|
73
73
|
}
|
|
@@ -78,7 +78,7 @@ export const memoSimple =
|
|
|
78
78
|
|
|
79
79
|
if (logMiss) {
|
|
80
80
|
logger.log(
|
|
81
|
-
`${methodSignature}(${_getArgsSignature(args,
|
|
81
|
+
`${methodSignature}(${_getArgsSignature(args, logArgs)}) @memo miss (${
|
|
82
82
|
Date.now() - d
|
|
83
83
|
} ms)`,
|
|
84
84
|
)
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from './decorators/memo.decorator'
|
|
|
15
15
|
export * from './decorators/asyncMemo.decorator'
|
|
16
16
|
import { MemoCache, AsyncMemoCache } from './decorators/memo.util'
|
|
17
17
|
export * from './decorators/memoFn'
|
|
18
|
+
export * from './decorators/memoFnAsync'
|
|
18
19
|
export * from './decorators/retry.decorator'
|
|
19
20
|
export * from './decorators/timeout.decorator'
|
|
20
21
|
export * from './error/app.error'
|