@@ -4,14 +4,17 @@ import { NamedLogger } from '../logger/logger';
4
4
const logger = new NamedLogger ( 'zones' ) ;
5
5
6
6
type AsyncHooks = typeof AsyncHooksModule ;
7
+ type AsyncResource = AsyncHooksModule . AsyncResource ;
7
8
8
9
export type ZoneId = number ;
9
10
10
11
const isNode = ( ) => typeof window === 'undefined' ;
11
12
12
13
let nodeAsyncHooksModulePromise : Promise < AsyncHooks > | undefined ;
14
+ let asyncHooks ! : AsyncHooks ;
15
+
13
16
export const asyncHooksZonator = async ( ) => {
14
- const asyncHooks = await ( nodeAsyncHooksModulePromise ??= import ( 'node:async_hooks' ) ) ;
17
+ asyncHooks ?? = await ( nodeAsyncHooksModulePromise ??= import ( 'node:async_hooks' ) ) ;
15
18
const zoneIndex = new Map < number , ZoneId > ( ) ;
16
19
let zoneId : ZoneId = 0 ;
17
20
asyncHooks
@@ -20,21 +23,30 @@ export const asyncHooksZonator = async () => {
20
23
zoneIndex . set ( asyncId , zoneIndex . get ( triggerAsyncId ) ?? 0 ) ;
21
24
} ,
22
25
before ( asyncId ) {
26
+ // Called only before start new async context execution
23
27
zoneId = zoneIndex . get ( asyncId ) ?? 0 ; // root zone
24
28
} ,
29
+ after ( ) {
30
+ // Called only after current async context execution backwards one level up
31
+ zoneId = zoneIndex . get ( asyncHooks . triggerAsyncId ( ) ) ?? 0 ; // root zone
32
+ } ,
25
33
destroy ( asyncId ) {
26
34
zoneIndex . delete ( asyncId ) ;
27
35
} ,
28
36
} )
29
37
. enable ( ) ;
30
38
return {
31
39
getZoneId : ( ) => zoneId ,
32
- ensureZone : ( ) => {
33
- zoneId = asyncHooks . executionAsyncId ( ) ;
34
- zoneIndex . set ( zoneId , zoneId ) ;
40
+ ensureZone : ( overrideZoneId ?: ZoneId ) => {
41
+ if ( overrideZoneId === undefined ) {
42
+ zoneId = asyncHooks . executionAsyncId ( ) ;
43
+ zoneIndex . set ( zoneId , zoneId ) ;
44
+ } else {
45
+ zoneIndex . set ( overrideZoneId , overrideZoneId ) ;
46
+ }
35
47
} ,
36
- createCheckDestroy : ( ) => {
37
- const keepZoneId = zoneId ;
48
+ createCheckDestroy : ( overrideZoneId ?: ZoneId ) => {
49
+ const keepZoneId = overrideZoneId ?? zoneId ;
38
50
return ( ) => {
39
51
if ( zoneIndex . has ( keepZoneId ) ) {
40
52
logger . warn ( 'Isolation destroyed but zone has not fulfilled async resources' ) ;
@@ -57,11 +69,11 @@ export const initAsyncHooksZonator = async () => {
57
69
58
70
export interface Zonator {
59
71
getZoneId ( ) : ZoneId ;
60
- ensureZone ( ) : void ; // Function for set current zone_id when new isolate starts
61
- createCheckDestroy ( ) : ( ) => void ;
72
+ ensureZone ( overrideZoneId ?: ZoneId ) : void ; // Function for set current zone_id when new isolate starts
73
+ createCheckDestroy ( overrideZoneId ?: ZoneId ) : ( ) => void ;
62
74
}
63
75
64
- let zonator : Zonator | undefined ;
76
+ let zonator ! : Zonator ;
65
77
66
78
export const getZoneId = ( ) => zonator ?. getZoneId ( ) ?? 0 ;
67
79
@@ -86,3 +98,36 @@ export const createIsolate =
86
98
zonePromise . finally ( ( ) => checkZoneDestroy ( ) ) ;
87
99
return zonePromise ;
88
100
} ;
101
+
102
+ export const createIsolatedAsyncResourceClass = ( destroy : ( ) => void ) => {
103
+ return class IsolatedAsyncResource {
104
+ private resource : AsyncResource ;
105
+ private zoneId : ZoneId ;
106
+ private checkZoneDestroy : ( ) => void ;
107
+
108
+ runInAsyncScope < This , A extends unknown [ ] , R = unknown > (
109
+ target : ( this : This , ...args : A ) => R ,
110
+ thisArg ?: This ,
111
+ ...args : A
112
+ ) : R {
113
+ return this . resource . runInAsyncScope ( target , thisArg , ...args ) ;
114
+ }
115
+
116
+ constructor ( name = 'IsolatedAsyncResource' ) {
117
+ if ( ! zonator ) {
118
+ throw new Error ( 'initAsyncHooksZonator should be called for isolate usage' ) ;
119
+ }
120
+ this . resource = new asyncHooks . AsyncResource ( name , { requireManualDestroy : true } ) ;
121
+ this . zoneId = this . resource . asyncId ( ) ;
122
+ zonator . ensureZone ( this . zoneId ) ;
123
+ this . checkZoneDestroy = zonator . createCheckDestroy ( this . zoneId ) ;
124
+ }
125
+
126
+ emitDestroy ( ) : this {
127
+ this . runInAsyncScope ( destroy ) ;
128
+ this . resource . emitDestroy ( ) ;
129
+ this . checkZoneDestroy ( ) ;
130
+ return this ;
131
+ }
132
+ } ;
133
+ } ;
0 commit comments