[Bridge] Consistently post "DidFailToLoad" notification when there's an error

Summary:
Previously the bridge sometimes never fired RCTJavaScriptDidLoadNotification or RCTJavaScriptDidFailToLoadNotification if there was an error (for example, if the source code loaded but we couldn't inject the JSON config). This diff moves the error handling into a method called `stopLoadingWithError` that the bridge can call whenever there is an error.

Also if the script failed to load, the BatchedBridge still called `executeSourceCode`. With this diff the `_loading` flag is set to NO when the script fails to load, and `executeSourceCode` returns immediately when `_loading` is false. This way the bridge does not try to execute JS when there is a loading error.
Closes https://github.com/facebook/react-native/pull/2520
Github Author: James Ide <ide@jameside.com>
This commit is contained in:
James Ide 2015-09-04 03:19:30 -07:00
parent 9b1f6c9e30
commit 3f4c7e40c6
1 changed files with 46 additions and 31 deletions

View File

@ -117,8 +117,15 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
dispatch_group_t initModulesAndLoadSource = dispatch_group_create(); dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
dispatch_group_enter(initModulesAndLoadSource); dispatch_group_enter(initModulesAndLoadSource);
__weak RCTBatchedBridge *weakSelf = self;
__block NSString *sourceCode; __block NSString *sourceCode;
[self loadSource:^(__unused NSError *error, NSString *source) { [self loadSource:^(NSError *error, NSString *source) {
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf stopLoadingWithError:error];
});
}
sourceCode = source; sourceCode = source;
dispatch_group_leave(initModulesAndLoadSource); dispatch_group_leave(initModulesAndLoadSource);
}]; }];
@ -131,7 +138,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
RCTProfileHookModules(self); RCTProfileHookModules(self);
} }
__weak RCTBatchedBridge *weakSelf = self;
__block NSString *config; __block NSString *config;
dispatch_group_enter(initModulesAndLoadSource); dispatch_group_enter(initModulesAndLoadSource);
dispatch_async(bridgeQueue, ^{ dispatch_async(bridgeQueue, ^{
@ -150,16 +156,24 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
// We're not waiting for this complete to leave the dispatch group, since // We're not waiting for this complete to leave the dispatch group, since
// injectJSONConfiguration and executeSourceCode will schedule operations on the // injectJSONConfiguration and executeSourceCode will schedule operations on the
// same queue anyway. // same queue anyway.
[weakSelf injectJSONConfiguration:config onComplete:^(__unused NSError *error) { [weakSelf injectJSONConfiguration:config onComplete:^(NSError *error) {
RCTPerformanceLoggerEnd(RCTPLNativeModuleInit); RCTPerformanceLoggerEnd(RCTPLNativeModuleInit);
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf stopLoadingWithError:error];
});
}
}]; }];
dispatch_group_leave(initModulesAndLoadSource); dispatch_group_leave(initModulesAndLoadSource);
}); });
}); });
dispatch_group_notify(initModulesAndLoadSource, bridgeQueue, ^{ dispatch_group_notify(initModulesAndLoadSource, dispatch_get_main_queue(), ^{
if (sourceCode) { RCTBatchedBridge *strongSelf = weakSelf;
[weakSelf executeSourceCode:sourceCode]; if (sourceCode && strongSelf.loading) {
dispatch_async(bridgeQueue, ^{
[weakSelf executeSourceCode:sourceCode];
});
} }
}); });
} }
@ -172,23 +186,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) { RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) {
RCTProfileEndAsyncEvent(0, @"init,download", cookie, @"JavaScript download", nil); RCTProfileEndAsyncEvent(0, @"init,download", cookie, @"JavaScript download", nil);
RCTPerformanceLoggerEnd(RCTPLScriptDownload); RCTPerformanceLoggerEnd(RCTPLScriptDownload);
if (error) {
NSArray *stack = error.userInfo[@"stack"];
if (stack) {
[self.redBox showErrorMessage:error.localizedDescription
withStack:stack];
} else {
[self.redBox showErrorMessage:error.localizedDescription
withDetails:error.localizedFailureReason];
}
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
object:_parentBridge
userInfo:userInfo];
}
_onSourceLoad(error, source); _onSourceLoad(error, source);
}; };
@ -283,7 +280,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
object:self]; object:self];
} }
- (void)setupExecutor - (void)setupExecutor
{ {
[_javaScriptExecutor setUp]; [_javaScriptExecutor setUp];
@ -313,12 +309,7 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
[_javaScriptExecutor injectJSONText:configJSON [_javaScriptExecutor injectJSONText:configJSON
asGlobalObjectNamed:@"__fbBatchedBridgeConfig" asGlobalObjectNamed:@"__fbBatchedBridgeConfig"
callback:^(NSError *error) { callback:onComplete];
if (error) {
[self.redBox showError:error];
}
onComplete(error);
}];
} }
- (void)executeSourceCode:(NSString *)sourceCode - (void)executeSourceCode:(NSString *)sourceCode
@ -333,7 +324,9 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:^(NSError *loadError) { [self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:^(NSError *loadError) {
if (loadError) { if (loadError) {
[self.redBox showError:loadError]; dispatch_async(dispatch_get_main_queue(), ^{
[self stopLoadingWithError:loadError];
});
return; return;
} }
@ -352,6 +345,28 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
}]; }];
} }
- (void)stopLoadingWithError:(NSError *)error
{
RCTAssertMainThread();
if (!self.isValid || !self.loading) {
return;
}
_loading = NO;
NSArray *stack = error.userInfo[@"stack"];
if (stack) {
[self.redBox showErrorMessage:error.localizedDescription withStack:stack];
} else {
[self.redBox showError:error];
}
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
object:_parentBridge
userInfo:userInfo];
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleURL RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleURL
moduleProvider:(__unused RCTBridgeModuleProviderBlock)block moduleProvider:(__unused RCTBridgeModuleProviderBlock)block