@@ -76,6 +76,9 @@ type OnyxMethod = ValueOf<typeof METHOD>;
7676let mergeQueue : Record < OnyxKey , Array < OnyxValue < OnyxKey > > > = { } ;
7777let mergeQueuePromise : Record < OnyxKey , Promise < void > > = { } ;
7878
79+ // Used to schedule subscriber update to the macro task queue
80+ let nextMacrotaskPromise : Promise < void > | null = null ;
81+
7982// Holds a mapping of all the React components that want their state subscribed to a store key
8083let callbackToStateMapping : Record < string , CallbackToStateMapping < OnyxKey > > = { } ;
8184
@@ -85,9 +88,6 @@ let onyxCollectionKeySet = new Set<OnyxKey>();
8588// Holds a mapping of the connected key to the subscriptionID for faster lookups
8689let onyxKeyToSubscriptionIDs = new Map ( ) ;
8790
88- // Keys with subscriptions currently being established
89- const pendingSubscriptionKeys = new Set < OnyxKey > ( ) ;
90-
9191// Optional user-provided key value states set when Onyx initializes or clears
9292let defaultKeyStates : Record < OnyxKey , OnyxValue < OnyxKey > > = { } ;
9393
@@ -804,29 +804,21 @@ function getCollectionDataAndSendAsObject<TKey extends OnyxKey>(matchingKeys: Co
804804 } ) ;
805805}
806806
807- /** Helps to schedule subscriber update. Schedule the macrotask if the key subscription is in progress to avoid race condition.
807+ /**
808+ * Delays promise resolution until the next macrotask to prevent race condition if the key subscription is in progress.
808809 *
809- * @param key Onyx key
810810 * @param callback The keyChanged/keysChanged callback
811811 * */
812- function prepareSubscriberUpdate < TKey extends OnyxKey > ( key : TKey , callback : ( ) => void ) : Promise < void > {
813- let collectionKey : string | undefined ;
814- try {
815- collectionKey = getCollectionKey ( key ) ;
816- } catch ( e ) {
817- // If getCollectionKey() throws an error it means the key is not a collection key.
818- collectionKey = undefined ;
819- }
820-
821- // If subscription is in progress, schedule a macrotask to prevent race condition with data from subscribeToKey deferred logic.
822- if ( pendingSubscriptionKeys . has ( key ) || ( collectionKey && pendingSubscriptionKeys . has ( collectionKey ) ) ) {
823- const macrotaskPromise = new Promise < void > ( ( resolve ) => {
824- setTimeout ( ( ) => resolve ( ) , 0 ) ;
812+ function prepareSubscriberUpdate ( callback : ( ) => void ) : Promise < void > {
813+ if ( ! nextMacrotaskPromise ) {
814+ nextMacrotaskPromise = new Promise < void > ( ( resolve ) => {
815+ setTimeout ( ( ) => {
816+ nextMacrotaskPromise = null ;
817+ resolve ( ) ;
818+ } , 0 ) ;
825819 } ) ;
826- return Promise . all ( [ macrotaskPromise , Promise . resolve ( ) . then ( callback ) ] ) . then ( ) ;
827820 }
828-
829- return Promise . resolve ( ) . then ( callback ) ;
821+ return Promise . all ( [ nextMacrotaskPromise , Promise . resolve ( ) . then ( callback ) ] ) . then ( ) ;
830822}
831823
832824/**
@@ -841,7 +833,7 @@ function scheduleSubscriberUpdate<TKey extends OnyxKey>(
841833 canUpdateSubscriber : ( subscriber ?: CallbackToStateMapping < OnyxKey > ) => boolean = ( ) => true ,
842834 isProcessingCollectionUpdate = false ,
843835) : Promise < void > {
844- return prepareSubscriberUpdate ( key , ( ) => keyChanged ( key , value , canUpdateSubscriber , isProcessingCollectionUpdate ) ) ;
836+ return prepareSubscriberUpdate ( ( ) => keyChanged ( key , value , canUpdateSubscriber , isProcessingCollectionUpdate ) ) ;
845837}
846838
847839/**
@@ -854,7 +846,7 @@ function scheduleNotifyCollectionSubscribers<TKey extends OnyxKey>(
854846 value : OnyxCollection < KeyValueMapping [ TKey ] > ,
855847 previousValue ?: OnyxCollection < KeyValueMapping [ TKey ] > ,
856848) : Promise < void > {
857- return prepareSubscriberUpdate ( key , ( ) => keysChanged ( key , value , previousValue ) ) ;
849+ return prepareSubscriberUpdate ( ( ) => keysChanged ( key , value , previousValue ) ) ;
858850}
859851
860852/**
@@ -1103,10 +1095,7 @@ function subscribeToKey<TKey extends OnyxKey>(connectOptions: ConnectOptions<TKe
11031095 // We create a mapping from key to lists of subscriptionIDs to access the specific list of subscriptionIDs.
11041096 storeKeyBySubscriptions ( mapping . key , callbackToStateMapping [ subscriptionID ] . subscriptionID ) ;
11051097
1106- pendingSubscriptionKeys . add ( mapping . key ) ;
1107-
11081098 if ( mapping . initWithStoredValues === false ) {
1109- pendingSubscriptionKeys . delete ( mapping . key ) ;
11101099 return subscriptionID ;
11111100 }
11121101
@@ -1152,7 +1141,8 @@ function subscribeToKey<TKey extends OnyxKey>(connectOptions: ConnectOptions<TKe
11521141
11531142 // Here we cannot use batching because the nullish value is expected to be set immediately for default props
11541143 // or they will be undefined.
1155- return sendDataToConnection ( mapping , null , undefined ) ;
1144+ sendDataToConnection ( mapping , null , undefined ) ;
1145+ return ;
11561146 }
11571147
11581148 // When using a callback subscriber we will either trigger the provided callback for each key we find or combine all values
@@ -1161,25 +1151,25 @@ function subscribeToKey<TKey extends OnyxKey>(connectOptions: ConnectOptions<TKe
11611151 if ( typeof mapping . callback === 'function' ) {
11621152 if ( isCollectionKey ( mapping . key ) ) {
11631153 if ( mapping . waitForCollectionCallback ) {
1164- return getCollectionDataAndSendAsObject ( matchingKeys , mapping ) ;
1154+ getCollectionDataAndSendAsObject ( matchingKeys , mapping ) ;
1155+ return ;
11651156 }
11661157
11671158 // We did not opt into using waitForCollectionCallback mode so the callback is called for every matching key.
1168- return multiGet ( matchingKeys ) . then ( ( values ) => {
1159+ multiGet ( matchingKeys ) . then ( ( values ) => {
11691160 values . forEach ( ( val , key ) => {
11701161 sendDataToConnection ( mapping , val as OnyxValue < TKey > , key as TKey ) ;
11711162 } ) ;
11721163 } ) ;
1164+ return ;
11731165 }
11741166
11751167 // If we are not subscribed to a collection key then there's only a single key to send an update for.
1176- return get ( mapping . key ) . then ( ( val ) => sendDataToConnection ( mapping , val as OnyxValue < TKey > , mapping . key ) ) ;
1168+ get ( mapping . key ) . then ( ( val ) => sendDataToConnection ( mapping , val as OnyxValue < TKey > , mapping . key ) ) ;
1169+ return ;
11771170 }
11781171
11791172 console . error ( 'Warning: Onyx.connect() was found without a callback' ) ;
1180- } )
1181- . then ( ( ) => {
1182- pendingSubscriptionKeys . delete ( mapping . key ) ;
11831173 } ) ;
11841174
11851175 // The subscriptionID is returned back to the caller so that it can be used to clean up the connection when it's no longer needed
0 commit comments