diff --git a/packages/react-native/Libraries/Animated/animations/Animation.js b/packages/react-native/Libraries/Animated/animations/Animation.js index ba7601c988b..28ba043b8c7 100644 --- a/packages/react-native/Libraries/Animated/animations/Animation.js +++ b/packages/react-native/Libraries/Animated/animations/Animation.js @@ -165,7 +165,11 @@ export default class Animation { const callback = this.#onEnd; if (callback != null) { this.#onEnd = null; - callback(result); + if (ReactNativeFeatureFlags.scheduleAnimatedEndCallbackInMicrotask()) { + queueMicrotask(() => callback(result)); + } else { + callback(result); + } } } } diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index e88b40fefce..6554c5b2862 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -541,6 +541,15 @@ const definitions: FeatureFlagDefinitions = { purpose: 'release', }, }, + scheduleAnimatedEndCallbackInMicrotask: { + defaultValue: false, + metadata: { + dateAdded: '2024-09-27', + description: + 'Changes the completion callback supplied via `Animation#start` to be scheduled in a microtask instead of synchronously executed.', + purpose: 'experimentation', + }, + }, shouldSkipStateUpdatesForLoopingAnimations: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 68cd66a4174..eefbd023fe3 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<36900baefd540f0fbb5920e9001b969c>> * @flow strict */ @@ -36,6 +36,7 @@ export type ReactNativeFeatureFlagsJsOnly = { enableAnimatedPropsMemo: Getter, enableOptimisedVirtualizedCells: Getter, isLayoutAnimationEnabled: Getter, + scheduleAnimatedEndCallbackInMicrotask: Getter, shouldSkipStateUpdatesForLoopingAnimations: Getter, shouldUseAnimatedObjectForTransform: Getter, shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter, @@ -146,6 +147,11 @@ export const enableOptimisedVirtualizedCells: Getter = createJavaScript */ export const isLayoutAnimationEnabled: Getter = createJavaScriptFlagGetter('isLayoutAnimationEnabled', true); +/** + * Changes the completion callback supplied via `Animation#start` to be scheduled in a microtask instead of synchronously executed. + */ +export const scheduleAnimatedEndCallbackInMicrotask: Getter = createJavaScriptFlagGetter('scheduleAnimatedEndCallbackInMicrotask', false); + /** * If the animation is within Animated.loop, we do not send state updates to React. */