@@ -672,6 +672,8 @@ - (NSString *)description
672672 */
673673static NSMutableDictionary *RCTLocalModuleIDs;
674674static NSMutableDictionary *RCTLocalMethodIDs;
675+ static NSMutableArray *RCTLocalModuleNames;
676+ static NSMutableArray *RCTLocalMethodNames;
675677static NSDictionary *RCTLocalModulesConfig ()
676678{
677679 static NSMutableDictionary *localModules;
@@ -680,6 +682,8 @@ - (NSString *)description
680682
681683 RCTLocalModuleIDs = [[NSMutableDictionary alloc ] init ];
682684 RCTLocalMethodIDs = [[NSMutableDictionary alloc ] init ];
685+ RCTLocalModuleNames = [[NSMutableArray alloc ] init ];
686+ RCTLocalMethodNames = [[NSMutableArray alloc ] init ];
683687
684688 localModules = [[NSMutableDictionary alloc ] init ];
685689 for (NSString *moduleDotMethod in RCTJSMethods ()) {
@@ -711,6 +715,8 @@ - (NSString *)description
711715 // Add module and method lookup
712716 RCTLocalModuleIDs[moduleDotMethod] = module[@" moduleID" ];
713717 RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@" methodID" ];
718+ [RCTLocalModuleNames addObject: moduleName];
719+ [RCTLocalMethodNames addObject: methodName];
714720 }
715721 });
716722
@@ -1072,7 +1078,7 @@ - (void)invalidate
10721078#pragma mark - RCTBridge methods
10731079
10741080/* *
1075- * Like JS::call, for objective-c .
1081+ * Public. Can be invoked from any thread .
10761082 */
10771083- (void )enqueueJSCall : (NSString *)moduleDotMethod args : (NSArray *)args
10781084{
@@ -1083,12 +1089,10 @@ - (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
10831089 NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
10841090 RCTAssert (methodID != nil , @" Method '%@ ' not registered." , moduleDotMethod);
10851091
1086- if (!_loading) {
1087- [self _invokeAndProcessModule: @" BatchedBridge"
1088- method: @" callFunctionReturnFlushedQueue"
1089- arguments: @[moduleID, methodID, args ?: @[]]
1090- context: RCTGetExecutorID (_javaScriptExecutor)];
1091- }
1092+ [self _invokeAndProcessModule: @" BatchedBridge"
1093+ method: @" callFunctionReturnFlushedQueue"
1094+ arguments: @[moduleID, methodID, args ?: @[]]
1095+ context: RCTGetExecutorID (_javaScriptExecutor)];
10921096}
10931097
10941098/* *
@@ -1106,10 +1110,17 @@ - (void)_immediatelyCallTimer:(NSNumber *)timer
11061110
11071111 if (!_loading) {
11081112#if BATCHED_BRIDGE
1109- [self _actuallyInvokeAndProcessModule: @" BatchedBridge"
1110- method: @" callFunctionReturnFlushedQueue"
1111- arguments: @[moduleID, methodID, @[@[timer]]]
1112- context: RCTGetExecutorID (_javaScriptExecutor)];
1113+ dispatch_block_t block = ^{
1114+ [self _actuallyInvokeAndProcessModule: @" BatchedBridge"
1115+ method: @" callFunctionReturnFlushedQueue"
1116+ arguments: @[moduleID, methodID, @[@[timer]]]
1117+ context: RCTGetExecutorID (_javaScriptExecutor)];
1118+ };
1119+ if ([_javaScriptExecutor respondsToSelector: @selector (executeAsyncBlockOnJavaScriptQueue: )]) {
1120+ [_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue: block];
1121+ } else {
1122+ [_javaScriptExecutor executeBlockOnJavaScriptQueue: block];
1123+ }
11131124
11141125#else
11151126
@@ -1163,33 +1174,83 @@ - (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID
11631174 }
11641175}
11651176
1177+ /* *
1178+ * Called by enqueueJSCall from any thread, or from _immediatelyCallTimer,
1179+ * on the JS thread, but only in non-batched mode.
1180+ */
11661181- (void )_invokeAndProcessModule : (NSString *)module method : (NSString *)method arguments : (NSArray *)args context : (NSNumber *)context
11671182{
11681183#if BATCHED_BRIDGE
1169- RCTProfileBeginEvent ();
11701184
1171- if ([module isEqualToString: @" RCTEventEmitter" ]) {
1172- for (NSDictionary *call in _scheduledCalls) {
1173- if ([call[@" module" ] isEqualToString: module] && [call[@" method" ] isEqualToString: method] && [call[@" args" ][0 ] isEqualToString: args[0 ]]) {
1174- [_scheduledCalls removeObject: call];
1185+ __weak NSMutableArray *weakScheduledCalls = _scheduledCalls;
1186+ __weak RCTSparseArray *weakScheduledCallbacks = _scheduledCallbacks;
1187+
1188+ [_javaScriptExecutor executeBlockOnJavaScriptQueue: ^{
1189+ RCTProfileBeginEvent ();
1190+
1191+ NSMutableArray *scheduledCalls = weakScheduledCalls;
1192+ RCTSparseArray *scheduledCallbacks = weakScheduledCallbacks;
1193+ if (!scheduledCalls || !scheduledCallbacks) {
1194+ return ;
1195+ }
1196+
1197+ /* *
1198+ * Event deduping
1199+ *
1200+ * Right now we make a lot of assumptions about the arguments structure
1201+ * so just iterate if it's a `callFunctionReturnFlushedQueue()`
1202+ */
1203+ if ([method isEqualToString: @" callFunctionReturnFlushedQueue" ]) {
1204+ NSString *moduleName = RCTLocalModuleNames[[args[0 ] integerValue ]];
1205+ /* *
1206+ * Keep going if it any event emmiter, e.g. RCT(Device|NativeApp)?EventEmitter
1207+ */
1208+ if ([moduleName hasSuffix: @" EventEmitter" ]) {
1209+ for (NSDictionary *call in [scheduledCalls copy ]) {
1210+ NSArray *callArgs = call[@" args" ];
1211+ /* *
1212+ * If it's the same module && method call on the bridge &&
1213+ * the same EventEmitter module && method
1214+ */
1215+ if (
1216+ [call[@" module" ] isEqualToString: module] &&
1217+ [call[@" method" ] isEqualToString: method] &&
1218+ [callArgs[0 ] isEqual: args[0 ]] &&
1219+ [callArgs[1 ] isEqual: args[1 ]]
1220+ ) {
1221+ /* *
1222+ * args[2] contains the actual arguments for the event call, where
1223+ * args[2][0] is the target for RCTEventEmitter or the eventName
1224+ * for the other EventEmitters
1225+ * if RCTEventEmitter we need to compare args[2][1] that will be
1226+ * the eventName
1227+ */
1228+ if (
1229+ [args[2 ][0 ] isEqual: callArgs[2 ][0 ]] &&
1230+ ([moduleName isEqualToString: @" RCTEventEmitter" ] ? [args[2 ][1 ] isEqual: callArgs[2 ][1 ]] : YES )
1231+ ) {
1232+ [scheduledCalls removeObject: call];
1233+ }
1234+ }
1235+ }
11751236 }
11761237 }
1177- }
11781238
1179- id call = @{
1180- @" module" : module,
1181- @" method" : method,
1182- @" args" : args,
1183- @" context" : context ?: @0 ,
1184- };
1239+ id call = @{
1240+ @" module" : module,
1241+ @" method" : method,
1242+ @" args" : args,
1243+ @" context" : context ?: @0 ,
1244+ };
11851245
1186- if ([method isEqualToString: @" invokeCallbackAndReturnFlushedQueue" ]) {
1187- _scheduledCallbacks [args[0 ]] = call;
1188- } else {
1189- [_scheduledCalls addObject: call];
1190- }
1246+ if ([method isEqualToString: @" invokeCallbackAndReturnFlushedQueue" ]) {
1247+ scheduledCallbacks [args[0 ]] = call;
1248+ } else {
1249+ [scheduledCalls addObject: call];
1250+ }
11911251
1192- RCTProfileEndEvent (@" enqueue_call" , @" objc_call" , call);
1252+ RCTProfileEndEvent (@" enqueue_call" , @" objc_call" , call);
1253+ }];
11931254}
11941255
11951256- (void )_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
0 commit comments