@@ -23,6 +23,7 @@ import type {EventSubscription} from '../../../Libraries/vendor/emitter/EventEmi
2323
2424import NativeAnimatedNonTurboModule from '../../../Libraries/Animated/NativeAnimatedModule' ;
2525import NativeAnimatedTurboModule from '../../../Libraries/Animated/NativeAnimatedTurboModule' ;
26+ import queueMicrotask from '../../../Libraries/Core/Timers/queueMicrotask' ;
2627import NativeEventEmitter from '../../../Libraries/EventEmitter/NativeEventEmitter' ;
2728import RCTDeviceEventEmitter from '../../../Libraries/EventEmitter/RCTDeviceEventEmitter' ;
2829import Platform from '../../../Libraries/Utilities/Platform' ;
@@ -57,7 +58,6 @@ const isSingleOpBatching =
5758 Platform.OS === 'android' &&
5859 NativeAnimatedModule ?. queueAndExecuteBatchedOperations != null &&
5960 ReactNativeFeatureFlags . animatedShouldUseSingleOp ( ) ;
60- let flushQueueImmediate = null ;
6161
6262const eventListenerGetValueCallbacks : {
6363 [ number ] : ( value : number ) => void ,
@@ -71,19 +71,18 @@ let globalEventEmitterAnimationFinishedListener: ?EventSubscription = null;
7171const shouldSignalBatch : boolean =
7272 ReactNativeFeatureFlags . cxxNativeAnimatedEnabled ( ) ;
7373
74- // Schedules `API.flushQueue` after the current batch, replacing any pending
75- // flush. On device `setImmediate` is a microtask; under jest's fake timers it's
76- // a fake-timer entry that only `runAllTimers` drains — not `await` or
77- // `advanceTimersByTime` — so the deferred flush wouldn't run before a test's
78- // assertions. Flush synchronously in tests instead.
74+ let flushQueueGeneration = 1 ;
7975function scheduleQueueFlush ( ) : void {
80- clearImmediate ( flushQueueImmediate ) ;
81- if ( process . env . NODE_ENV === 'test' ) {
82- // TODO: T275950736 - remove this path
76+ const generation = ++ flushQueueGeneration ;
77+ queueMicrotask ( ( ) => {
78+ if ( generation !== flushQueueGeneration ) {
79+ return ;
80+ }
8381 API . flushQueue ( ) ;
84- } else {
85- flushQueueImmediate = setImmediate ( API . flushQueue ) ;
86- }
82+ } ) ;
83+ }
84+ function cancelQueueFlush ( ) : void {
85+ flushQueueGeneration ++ ;
8786}
8887
8988function createNativeOperations ( ) : NonNullable < typeof NativeAnimatedModule > {
@@ -229,7 +228,6 @@ const API = {
229228 NativeAnimatedModule ,
230229 'Native animated module is not available' ,
231230 ) ;
232- flushQueueImmediate = null ;
233231
234232 if ( singleOpQueue . length === 0 ) {
235233 return ;
@@ -250,7 +248,6 @@ const API = {
250248 NativeAnimatedModule ,
251249 'Native animated module is not available' ,
252250 ) ;
253- flushQueueImmediate = null ;
254251
255252 if ( queue . length === 0 ) {
256253 return ;
@@ -310,11 +307,10 @@ const API = {
310307
311308 waitingForQueuedOperations.add(id);
312309 queueOperations = true;
313- if (
314- ReactNativeFeatureFlags.animatedShouldDebounceQueueFlush() &&
315- flushQueueImmediate
316- ) {
317- clearImmediate ( flushQueueImmediate ) ;
310+ // Entering explicit queue mode: drop any flush already scheduled so ops
311+ // accumulate until `disableQueue`.
312+ if (ReactNativeFeatureFlags.animatedShouldDebounceQueueFlush()) {
313+ cancelQueueFlush ( ) ;
318314 }
319315 } ,
320316 startAnimatingNode : ( isSingleOpBatching
0 commit comments