11'use strict' ;
22
33const {
4+ ArrayPrototypeIncludes,
5+ ArrayPrototypeIndexOf,
6+ ArrayPrototypePush,
7+ ArrayPrototypeSplice,
48 ArrayPrototypeUnshift,
59 FunctionPrototypeBind,
610 NumberIsSafeInteger,
@@ -11,15 +15,18 @@ const {
1115} = primordials ;
1216
1317const {
18+ ERR_ASYNC_CALLBACK ,
1419 ERR_ASYNC_RESOURCE_DOMAIN_REMOVED ,
1520 ERR_ASYNC_TYPE ,
1621 ERR_INVALID_ASYNC_ID ,
22+ ERR_INVALID_ARG_VALUE ,
1723} = require ( 'internal/errors' ) . codes ;
1824const {
1925 deprecate,
2026 kEmptyObject,
2127} = require ( 'internal/util' ) ;
2228const {
29+ validateBoolean,
2330 validateFunction,
2431 validateString,
2532} = require ( 'internal/validators' ) ;
@@ -30,13 +37,17 @@ const AsyncContextFrame = require('internal/async_context_frame');
3037// Get functions
3138// For userland AsyncResources, make sure to emit a destroy event when the
3239// resource gets gced.
33- const { registerDestroyHook } = internal_async_hooks ;
40+ const { registerDestroyHook, kNoPromiseHook } = internal_async_hooks ;
3441const {
3542 asyncWrap,
3643 executionAsyncId,
3744 triggerAsyncId,
3845 // Private API
3946 hasAsyncIdStack,
47+ getHookArrays,
48+ enableHooks,
49+ disableHooks,
50+ updatePromiseHookMode,
4051 executionAsyncResource,
4152 // Internal Embedder API
4253 newAsyncId,
@@ -52,12 +63,115 @@ const {
5263// Get symbols
5364const {
5465 async_id_symbol, trigger_async_id_symbol,
66+ init_symbol, before_symbol, after_symbol, destroy_symbol,
67+ promise_resolve_symbol,
5568} = internal_async_hooks . symbols ;
5669
70+ // Get constants
71+ const {
72+ kInit, kBefore, kAfter, kDestroy, kTotals, kPromiseResolve,
73+ } = internal_async_hooks . constants ;
74+
5775// Listener API //
5876
77+ class AsyncHook {
78+ constructor ( { init, before, after, destroy, promiseResolve, trackPromises } ) {
79+ if ( init !== undefined && typeof init !== 'function' )
80+ throw new ERR_ASYNC_CALLBACK ( 'hook.init' ) ;
81+ if ( before !== undefined && typeof before !== 'function' )
82+ throw new ERR_ASYNC_CALLBACK ( 'hook.before' ) ;
83+ if ( after !== undefined && typeof after !== 'function' )
84+ throw new ERR_ASYNC_CALLBACK ( 'hook.after' ) ;
85+ if ( destroy !== undefined && typeof destroy !== 'function' )
86+ throw new ERR_ASYNC_CALLBACK ( 'hook.destroy' ) ;
87+ if ( promiseResolve !== undefined && typeof promiseResolve !== 'function' )
88+ throw new ERR_ASYNC_CALLBACK ( 'hook.promiseResolve' ) ;
89+ if ( trackPromises !== undefined ) {
90+ validateBoolean ( trackPromises , 'trackPromises' ) ;
91+ }
92+
93+ this [ init_symbol ] = init ;
94+ this [ before_symbol ] = before ;
95+ this [ after_symbol ] = after ;
96+ this [ destroy_symbol ] = destroy ;
97+ this [ promise_resolve_symbol ] = promiseResolve ;
98+ if ( trackPromises === false ) {
99+ if ( promiseResolve ) {
100+ throw new ERR_INVALID_ARG_VALUE ( 'trackPromises' ,
101+ trackPromises , 'must not be false when promiseResolve is enabled' ) ;
102+ }
103+ this [ kNoPromiseHook ] = true ;
104+ } else {
105+ // Default to tracking promises for now.
106+ this [ kNoPromiseHook ] = false ;
107+ }
108+ }
109+
110+ enable ( ) {
111+ // The set of callbacks for a hook should be the same regardless of whether
112+ // enable()/disable() are run during their execution. The following
113+ // references are reassigned to the tmp arrays if a hook is currently being
114+ // processed.
115+ const { 0 : hooks_array , 1 : hook_fields } = getHookArrays ( ) ;
116+
117+ // Each hook is only allowed to be added once.
118+ if ( ArrayPrototypeIncludes ( hooks_array , this ) )
119+ return this ;
120+
121+ const prev_kTotals = hook_fields [ kTotals ] ;
122+
123+ // createHook() has already enforced that the callbacks are all functions,
124+ // so here simply increment the count of whether each callbacks exists or
125+ // not.
126+ hook_fields [ kTotals ] = hook_fields [ kInit ] += + ! ! this [ init_symbol ] ;
127+ hook_fields [ kTotals ] += hook_fields [ kBefore ] += + ! ! this [ before_symbol ] ;
128+ hook_fields [ kTotals ] += hook_fields [ kAfter ] += + ! ! this [ after_symbol ] ;
129+ hook_fields [ kTotals ] += hook_fields [ kDestroy ] += + ! ! this [ destroy_symbol ] ;
130+ hook_fields [ kTotals ] +=
131+ hook_fields [ kPromiseResolve ] += + ! ! this [ promise_resolve_symbol ] ;
132+ ArrayPrototypePush ( hooks_array , this ) ;
133+
134+ if ( prev_kTotals === 0 && hook_fields [ kTotals ] > 0 ) {
135+ enableHooks ( ) ;
136+ }
137+
138+ if ( ! this [ kNoPromiseHook ] ) {
139+ updatePromiseHookMode ( ) ;
140+ }
141+
142+ return this ;
143+ }
144+
145+ disable ( ) {
146+ const { 0 : hooks_array , 1 : hook_fields } = getHookArrays ( ) ;
147+
148+ const index = ArrayPrototypeIndexOf ( hooks_array , this ) ;
149+ if ( index === - 1 )
150+ return this ;
151+
152+ const prev_kTotals = hook_fields [ kTotals ] ;
153+
154+ hook_fields [ kTotals ] = hook_fields [ kInit ] -= + ! ! this [ init_symbol ] ;
155+ hook_fields [ kTotals ] += hook_fields [ kBefore ] -= + ! ! this [ before_symbol ] ;
156+ hook_fields [ kTotals ] += hook_fields [ kAfter ] -= + ! ! this [ after_symbol ] ;
157+ hook_fields [ kTotals ] += hook_fields [ kDestroy ] -= + ! ! this [ destroy_symbol ] ;
158+ hook_fields [ kTotals ] +=
159+ hook_fields [ kPromiseResolve ] -= + ! ! this [ promise_resolve_symbol ] ;
160+ ArrayPrototypeSplice ( hooks_array , index , 1 ) ;
161+
162+ if ( prev_kTotals > 0 && hook_fields [ kTotals ] === 0 ) {
163+ disableHooks ( ) ;
164+ }
165+
166+ return this ;
167+ }
168+ }
169+
170+
59171const createHook = deprecate (
60- internal_async_hooks . createHook ,
172+ function createHook ( fns ) {
173+ return new AsyncHook ( fns ) ;
174+ } ,
61175 'async_hooks.createHook() is deprecated. Use AsyncLocalStorage for async ' +
62176 'context tracking and diagnostics_channel for tracing instead.' ,
63177 'DEP0212' ,
0 commit comments