@@ -29,19 +29,6 @@ type PatchedEventEmitter = {
29
29
__ot_listeners ?: { [ name : string ] : WeakMap < Func < void > , Func < void > > } ;
30
30
} & EventEmitter ;
31
31
32
- class Reference < T > {
33
- constructor ( private _value : T ) { }
34
-
35
- set ( value : T ) {
36
- this . _value = value ;
37
- return this ;
38
- }
39
-
40
- get ( ) {
41
- return this . _value ;
42
- }
43
- }
44
-
45
32
const ADD_LISTENER_METHODS = [
46
33
'addListener' as 'addListener' ,
47
34
'on' as 'on' ,
@@ -52,72 +39,36 @@ const ADD_LISTENER_METHODS = [
52
39
53
40
export class AsyncHooksContextManager implements ContextManager {
54
41
private _asyncHook : asyncHooks . AsyncHook ;
55
- private _contextRefs : Map < number , Reference < Context > | undefined > = new Map ( ) ;
42
+ private _contexts : Map < number , Context | undefined > = new Map ( ) ;
43
+ private _stack : Array < Context | undefined > = [ ] ;
56
44
57
45
constructor ( ) {
58
46
this . _asyncHook = asyncHooks . createHook ( {
59
47
init : this . _init . bind ( this ) ,
48
+ before : this . _before . bind ( this ) ,
49
+ after : this . _after . bind ( this ) ,
60
50
destroy : this . _destroy . bind ( this ) ,
61
51
promiseResolve : this . _destroy . bind ( this ) ,
62
52
} ) ;
63
53
}
64
54
65
55
active ( ) : Context {
66
- const ref = this . _contextRefs . get ( asyncHooks . executionAsyncId ( ) ) ;
67
- return ref === undefined ? Context . ROOT_CONTEXT : ref . get ( ) ;
56
+ return this . _stack [ this . _stack . length - 1 ] ?? Context . ROOT_CONTEXT ;
68
57
}
69
58
70
59
with < T extends ( ...args : unknown [ ] ) => ReturnType < T > > (
71
60
context : Context ,
72
61
fn : T
73
62
) : ReturnType < T > {
74
- const uid = asyncHooks . executionAsyncId ( ) ;
75
- let ref = this . _contextRefs . get ( uid ) ;
76
- let oldContext : Context | undefined = undefined ;
77
- if ( ref === undefined ) {
78
- ref = new Reference ( context ) ;
79
- this . _contextRefs . set ( uid , ref ) ;
80
- } else {
81
- oldContext = ref . get ( ) ;
82
- ref . set ( context ) ;
83
- }
63
+ this . _enterContext ( context ) ;
84
64
try {
85
65
return fn ( ) ;
86
66
} finally {
87
- if ( oldContext === undefined ) {
88
- this . _destroy ( uid ) ;
89
- } else {
90
- ref . set ( oldContext ) ;
91
- }
92
- }
93
- }
94
-
95
- async withAsync < T extends Promise < any > , U extends ( ...args : unknown [ ] ) => T > (
96
- context : Context ,
97
- fn : U
98
- ) : Promise < T > {
99
- const uid = asyncHooks . executionAsyncId ( ) ;
100
- let ref = this . _contextRefs . get ( uid ) ;
101
- let oldContext : Context | undefined = undefined ;
102
- if ( ref === undefined ) {
103
- ref = new Reference ( context ) ;
104
- this . _contextRefs . set ( uid , ref ) ;
105
- } else {
106
- oldContext = ref . get ( ) ;
107
- ref . set ( context ) ;
108
- }
109
- try {
110
- return await fn ( ) ;
111
- } finally {
112
- if ( oldContext === undefined ) {
113
- this . _destroy ( uid ) ;
114
- } else {
115
- ref . set ( oldContext ) ;
116
- }
67
+ this . _exitContext ( ) ;
117
68
}
118
69
}
119
70
120
- bind < T > ( target : T , context : Context ) : T {
71
+ bind < T > ( target : T , context ? : Context ) : T {
121
72
// if no specific context to propagate is given, we use the current one
122
73
if ( context === undefined ) {
123
74
context = this . active ( ) ;
@@ -137,7 +88,8 @@ export class AsyncHooksContextManager implements ContextManager {
137
88
138
89
disable ( ) : this {
139
90
this . _asyncHook . disable ( ) ;
140
- this . _contextRefs . clear ( ) ;
91
+ this . _contexts . clear ( ) ;
92
+ this . _stack = [ ] ;
141
93
return this ;
142
94
}
143
95
@@ -156,6 +108,7 @@ export class AsyncHooksContextManager implements ContextManager {
156
108
* It isn't possible to tell Typescript that contextWrapper is the same as T
157
109
* so we forced to cast as any here.
158
110
*/
111
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
159
112
return contextWrapper as any ;
160
113
}
161
114
@@ -271,9 +224,9 @@ export class AsyncHooksContextManager implements ContextManager {
271
224
* @param uid id of the async context
272
225
*/
273
226
private _init ( uid : number ) {
274
- const ref = this . _contextRefs . get ( asyncHooks . executionAsyncId ( ) ) ;
275
- if ( ref !== undefined ) {
276
- this . _contextRefs . set ( uid , ref ) ;
227
+ const context = this . _stack [ this . _stack . length - 1 ] ;
228
+ if ( context !== undefined ) {
229
+ this . _contexts . set ( uid , context ) ;
277
230
}
278
231
}
279
232
@@ -283,6 +236,38 @@ export class AsyncHooksContextManager implements ContextManager {
283
236
* @param uid uid of the async context
284
237
*/
285
238
private _destroy ( uid : number ) {
286
- this . _contextRefs . delete ( uid ) ;
239
+ this . _contexts . delete ( uid ) ;
240
+ }
241
+
242
+ /**
243
+ * Before hook is called just beforing executing a async context.
244
+ * @param uid uid of the async context
245
+ */
246
+ private _before ( uid : number ) {
247
+ const context = this . _contexts . get ( uid ) ;
248
+ if ( context !== undefined ) {
249
+ this . _enterContext ( context ) ;
250
+ }
251
+ }
252
+
253
+ /**
254
+ * After hook is called just after completing the execution of a async context.
255
+ */
256
+ private _after ( ) {
257
+ this . _exitContext ( ) ;
258
+ }
259
+
260
+ /**
261
+ * Set the given context as active
262
+ */
263
+ private _enterContext ( context : Context ) {
264
+ this . _stack . push ( context ) ;
265
+ }
266
+
267
+ /**
268
+ * Remove the context at the root of the stack
269
+ */
270
+ private _exitContext ( ) {
271
+ this . _stack . pop ( ) ;
287
272
}
288
273
}
0 commit comments