mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-03-26 23:24:06 +08:00
[ReactNative] Add completionBlock to -[RCTBridge enqueueJSCall:args:]
Summary: @public Allow to pass an optional completion block to the bridge JS calls. The block will be called from the JS thread, after the javascript has finished running and the returned calls have been processed/dispatched to the native modules. Test Plan: Added `testCallbackIsCalledOnTheRightTime` to `RKBatchedBridgeTests`
This commit is contained in:
@@ -74,6 +74,17 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
|
||||
*/
|
||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args;
|
||||
|
||||
/**
|
||||
* This is the same `enqueueJSCall:args:` but with a handler to notify that JS
|
||||
* has finished executing the call
|
||||
*
|
||||
* NOTE: The `completionHandler` will be called on the *JS* thread, so any expensive
|
||||
* calls should be avoided here
|
||||
*/
|
||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod
|
||||
withArguments:(NSArray *)arguments
|
||||
completionBlock:(void (^)(void))completionBlock;
|
||||
|
||||
/**
|
||||
* This macro is used to register a JS method to be called via the enqueueJSCall
|
||||
* bridge method. You should place this macro inside any file that uses the
|
||||
|
||||
@@ -214,7 +214,8 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||
- (void)_invokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
context:(NSNumber *)context;
|
||||
context:(NSNumber *)context
|
||||
completionBlock:(void (^)(void))completionBlock;
|
||||
|
||||
@end
|
||||
|
||||
@@ -227,7 +228,8 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||
- (void)_actuallyInvokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
context:(NSNumber *)context;
|
||||
context:(NSNumber *)context
|
||||
completionBlock:(void (^)(void))completionBlock;
|
||||
|
||||
@end
|
||||
|
||||
@@ -346,7 +348,8 @@ static NSString *RCTStringUpToFirstArgument(NSString *methodName)
|
||||
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"invokeCallbackAndReturnFlushedQueue"
|
||||
arguments:@[json, args]
|
||||
context:context];
|
||||
context:context
|
||||
completionBlock:nil];
|
||||
} : ^(NSArray *unused) {});
|
||||
)
|
||||
};
|
||||
@@ -882,10 +885,25 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
||||
{
|
||||
[self.batchedBridge enqueueJSCall:moduleDotMethod args:args];
|
||||
[self.batchedBridge enqueueJSCall:moduleDotMethod
|
||||
withArguments:args
|
||||
completionBlock:nil];
|
||||
}
|
||||
|
||||
RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context)
|
||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod
|
||||
withArguments:(NSArray *)arguments
|
||||
completionBlock:(void (^)(void))completionBlock
|
||||
{
|
||||
[self.batchedBridge enqueueJSCall:moduleDotMethod
|
||||
withArguments:arguments
|
||||
completionBlock:completionBlock];
|
||||
}
|
||||
|
||||
RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
context:(NSNumber *)context
|
||||
completionBlock:(void (^)(void))completionBlock)
|
||||
|
||||
@end
|
||||
|
||||
@@ -1093,6 +1111,7 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||
// Allow testing without a script
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_loading = NO;
|
||||
[_jsDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||
object:_parentBridge
|
||||
userInfo:@{ @"bridge": self }];
|
||||
@@ -1234,6 +1253,18 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||
* Public. Can be invoked from any thread.
|
||||
*/
|
||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
||||
{
|
||||
[self enqueueJSCall:moduleDotMethod
|
||||
withArguments:args
|
||||
completionBlock:nil];
|
||||
}
|
||||
|
||||
/**
|
||||
* Public. Can be invoked from any thread.
|
||||
*/
|
||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod
|
||||
withArguments:(NSArray *)arguments
|
||||
completionBlock:(void (^)(void))completionBlock
|
||||
{
|
||||
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
|
||||
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
|
||||
@@ -1244,8 +1275,9 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||
|
||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID ?: @0, methodID ?: @0, args ?: @[]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
arguments:@[moduleID ?: @0, methodID ?: @0, arguments ?: @[]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)
|
||||
completionBlock:completionBlock];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1267,7 +1299,8 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID, methodID, @[@[timer]]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)
|
||||
completionBlock:nil];
|
||||
};
|
||||
|
||||
if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) {
|
||||
@@ -1333,7 +1366,11 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||
* Called by enqueueJSCall from any thread, or from _immediatelyCallTimer,
|
||||
* on the JS thread, but only in non-batched mode.
|
||||
*/
|
||||
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
||||
- (void)_invokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
context:(NSNumber *)context
|
||||
completionBlock:(void (^)(void))completionBlock
|
||||
{
|
||||
/**
|
||||
* AnyThread
|
||||
@@ -1349,10 +1386,13 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||
}
|
||||
|
||||
id call = @{
|
||||
@"module": module,
|
||||
@"method": method,
|
||||
@"args": args,
|
||||
@"js_args": @{
|
||||
@"module": module,
|
||||
@"method": method,
|
||||
@"args": args,
|
||||
},
|
||||
@"context": context ?: @0,
|
||||
@"callback": (id)completionBlock ?: [NSNull null],
|
||||
};
|
||||
|
||||
if ([method isEqualToString:@"invokeCallbackAndReturnFlushedQueue"]) {
|
||||
@@ -1365,7 +1405,11 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
||||
- (void)_actuallyInvokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
context:(NSNumber *)context
|
||||
completionBlock:(void (^)(void))completionBlock
|
||||
{
|
||||
RCTAssertJSThread();
|
||||
|
||||
@@ -1377,6 +1421,10 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil];
|
||||
[self _handleBuffer:json context:context];
|
||||
|
||||
if (completionBlock) {
|
||||
completionBlock();
|
||||
}
|
||||
};
|
||||
|
||||
[_javaScriptExecutor executeJSCall:module
|
||||
@@ -1545,12 +1593,23 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||
return [call[@"context"] isEqualToNumber:currentExecutorID];
|
||||
}]];
|
||||
if (calls.count > 0) {
|
||||
void (^completionBlock)(void) = ^{
|
||||
for (NSDictionary *call in calls) {
|
||||
id callback = call[@"callback"];
|
||||
|
||||
if (callback && callback != [NSNull null]) {
|
||||
((void (^)(void))callback)();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_scheduledCalls = [[NSMutableArray alloc] init];
|
||||
_scheduledCallbacks = [[RCTSparseArray alloc] init];
|
||||
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"processBatch"
|
||||
arguments:@[calls]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
arguments:@[[calls valueForKey:@"js_args"]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)
|
||||
completionBlock:completionBlock];
|
||||
}
|
||||
|
||||
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
|
||||
|
||||
@@ -55,3 +55,12 @@ RCT_EXTERN BOOL RCTRunningInTestEnvironment(void);
|
||||
|
||||
// Return YES if image has an alpha component
|
||||
RCT_EXTERN BOOL RCTImageHasAlpha(CGImageRef image);
|
||||
|
||||
/**
|
||||
* Helper for async tests, run the runloop while the condition becomes true or
|
||||
* until timeout
|
||||
*/
|
||||
#define RCTRunLoopRunWhile(condition, timeout) \
|
||||
_RCTRunLoopRunWhile(^BOOL{ return condition; }, timeout)
|
||||
|
||||
RCT_EXTERN BOOL _RCTRunLoopRunWhile(BOOL (^)(void), NSTimeInterval);
|
||||
|
||||
@@ -273,3 +273,14 @@ BOOL RCTImageHasAlpha(CGImageRef image)
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL _RCTRunLoopRunWhile(BOOL (^block)(void), NSTimeInterval timeout)
|
||||
{
|
||||
NSDate *timeoutDate = [[NSDate date] dateByAddingTimeInterval:timeout];
|
||||
|
||||
while (block() && [timeoutDate timeIntervalSinceNow] > 0) {
|
||||
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
|
||||
}
|
||||
|
||||
return !block();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user