@@ -87,6 +87,9 @@ const {
8787 immediateInfo,
8888 timeoutInfo,
8989} = binding ;
90+ const {
91+ enqueueMicrotask,
92+ } = internalBinding ( 'task_queue' ) ;
9093
9194const {
9295 getDefaultTriggerAsyncId,
@@ -97,6 +100,9 @@ const {
97100 emitBefore,
98101 emitAfter,
99102 emitDestroy,
103+ symbols : {
104+ async_local_storage_context_symbol,
105+ } ,
100106} = require ( 'internal/async_hooks' ) ;
101107
102108// Symbols for storing async id state.
@@ -125,6 +131,39 @@ const AsyncContextFrame = require('internal/async_context_frame');
125131
126132const async_context_frame = Symbol ( 'kAsyncContextFrame' ) ;
127133
134+ function removeStoresFromResource ( resource ) {
135+ if ( AsyncContextFrame . enabled ) {
136+ if ( resource [ async_context_frame ] !== undefined ) {
137+ resource [ async_context_frame ] = undefined ;
138+ }
139+ } else if ( resource [ async_local_storage_context_symbol ] !== undefined ) {
140+ resource [ async_local_storage_context_symbol ] = undefined ;
141+ }
142+ }
143+
144+ function cleanTimer ( timer ) {
145+ removeStoresFromResource ( timer ) ;
146+ timer . _onTimeout = undefined ;
147+ timer . _timerArgs = undefined ;
148+ }
149+
150+ function cleanImmediate ( immediate ) {
151+ removeStoresFromResource ( immediate ) ;
152+ immediate . _onImmediate = undefined ;
153+ immediate . _argv = undefined ;
154+ }
155+
156+ function enqueueRemoveStoresFromResource ( resource ) {
157+ enqueueMicrotask ( ( ) => removeStoresFromResource ( resource ) ) ;
158+ }
159+
160+ function enqueueRemoveStoresIfNotReinserted ( resource ) {
161+ enqueueMicrotask ( ( ) => {
162+ if ( ! resource . _idleNext && ! resource . _idlePrev )
163+ removeStoresFromResource ( resource ) ;
164+ } ) ;
165+ }
166+
128167// *Must* match Environment::ImmediateInfo::Fields in src/env.h.
129168const kCount = 0 ;
130169const kRefCount = 1 ;
@@ -528,14 +567,22 @@ function getTimerCallbacks(runNextTicks) {
528567 const asyncId = immediate [ async_id_symbol ] ;
529568 emitBefore ( asyncId , immediate [ trigger_async_id_symbol ] , immediate ) ;
530569
570+ let threw = true ;
531571 try {
532572 const argv = immediate . _argv ;
533573 if ( ! argv )
534574 immediate . _onImmediate ( ) ;
535575 else
536576 immediate . _onImmediate ( ...argv ) ;
577+ threw = false ;
537578 } finally {
538- immediate . _onImmediate = null ;
579+ if ( threw ) {
580+ immediate . _onImmediate = undefined ;
581+ immediate . _argv = undefined ;
582+ enqueueRemoveStoresFromResource ( immediate ) ;
583+ } else {
584+ cleanImmediate ( immediate ) ;
585+ }
539586
540587 emitDestroy ( asyncId ) ;
541588
@@ -607,6 +654,8 @@ function getTimerCallbacks(runNextTicks) {
607654 if ( ! timer . _destroyed ) {
608655 timer . _destroyed = true ;
609656
657+ cleanTimer ( timer ) ;
658+
610659 if ( timer [ kHasPrimitive ] )
611660 delete knownTimersById [ asyncId ] ;
612661
@@ -629,26 +678,42 @@ function getTimerCallbacks(runNextTicks) {
629678 start = binding . getLibuvNow ( ) ;
630679 }
631680
681+ let threw = true ;
632682 try {
633683 const args = timer . _timerArgs ;
634684 if ( args === undefined )
635685 timer . _onTimeout ( ) ;
636686 else
637687 ReflectApply ( timer . _onTimeout , timer , args ) ;
688+ threw = false ;
638689 } finally {
639690 if ( timer . _repeat && timer . _idleTimeout !== - 1 ) {
640691 timer . _idleTimeout = timer . _repeat ;
641692 insert ( timer , timer . _idleTimeout , start ) ;
642- } else if ( ! timer . _idleNext && ! timer . _idlePrev && ! timer . _destroyed ) {
643- timer . _destroyed = true ;
644-
645- if ( timer [ kHasPrimitive ] )
646- delete knownTimersById [ asyncId ] ;
647-
648- if ( timer [ kRefed ] )
649- timeoutInfo [ 0 ] -- ;
650-
651- emitDestroy ( asyncId ) ;
693+ } else if ( ! timer . _idleNext && ! timer . _idlePrev ) {
694+ if ( timer . _destroyed ) {
695+ timer . _onTimeout = undefined ;
696+ timer . _timerArgs = undefined ;
697+ if ( threw )
698+ enqueueRemoveStoresIfNotReinserted ( timer ) ;
699+ else
700+ removeStoresFromResource ( timer ) ;
701+ } else {
702+ if ( threw )
703+ enqueueRemoveStoresIfNotReinserted ( timer ) ;
704+ else
705+ removeStoresFromResource ( timer ) ;
706+
707+ timer . _destroyed = true ;
708+
709+ if ( timer [ kHasPrimitive ] )
710+ delete knownTimersById [ asyncId ] ;
711+
712+ if ( timer [ kRefed ] )
713+ timeoutInfo [ 0 ] -- ;
714+
715+ emitDestroy ( asyncId ) ;
716+ }
652717 }
653718 }
654719
@@ -728,8 +793,11 @@ module.exports = {
728793 kTimeout : Symbol ( 'timeout' ) , // For hiding Timeouts on other internals.
729794 async_id_symbol,
730795 trigger_async_id_symbol,
796+ async_context_frame,
731797 Timeout,
732798 Immediate,
799+ cleanImmediate,
800+ cleanTimer,
733801 kRefed,
734802 kHasPrimitive,
735803 initAsyncResource,
0 commit comments