Browse files

Queue JS calls that come in before JS bundle has started loading inst…

…ead of crashing

Summary: This mimics (some of) the behavior we have on iOS where if you call a JS module method before the JS bundle has started loading, we just queue up those calls and execute them after the bundle has started loading.

Reviewed By: javache

Differential Revision: D4117581

fbshipit-source-id: 58c5a6f87aeeb86083385334d92f2716a0574ba1
  • Loading branch information...
astreet authored and Facebook Github Bot committed Nov 3, 2016
1 parent 52d90da commit 68aeffe01f442a67a53dd0eff43e9357b0ac3b22
@@ -40,7 +40,6 @@ void callFunction(
void destroy();
boolean isDestroyed();
boolean isAcceptingCalls();
* Initialize all the native modules
@@ -40,7 +40,8 @@
private static final String EARLY_JS_ACCESS_EXCEPTION_MESSAGE =
"Tried to access a JS module before the React instance was fully set up. Calls to " +
"ReactContext#getJSModule should be protected by ReactContext#hasActiveCatalystInstance().";
"ReactContext#getJSModule should only happen once initialize() has been called on your " +
"native module.";
private final CopyOnWriteArraySet<LifecycleEventListener> mLifecycleEventListeners =
new CopyOnWriteArraySet<>();
@@ -143,7 +144,7 @@ public CatalystInstance getCatalystInstance() {
public boolean hasActiveCatalystInstance() {
return mCatalystInstance != null && mCatalystInstance.isAcceptingCalls();
return mCatalystInstance != null && !mCatalystInstance.isDestroyed();
public LifecycleState getLifecycleState() {
@@ -12,6 +12,7 @@
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
@@ -57,6 +58,25 @@
private static final AtomicInteger sNextInstanceIdForTrace = new AtomicInteger(1);
private static class PendingJSCall {
public ExecutorToken mExecutorToken;
public String mModule;
public String mMethod;
public NativeArray mArguments;
public PendingJSCall(
ExecutorToken executorToken,
String module,
String method,
NativeArray arguments) {
mExecutorToken = executorToken;
mModule = module;
mMethod = method;
mArguments = arguments;
// Access from any thread
private final ReactQueueConfigurationImpl mReactQueueConfiguration;
private final CopyOnWriteArrayList<NotThreadSafeBridgeIdleDebugListener> mBridgeIdleListeners;
@@ -67,6 +87,8 @@
private final TraceListener mTraceListener;
private final JavaScriptModuleRegistry mJSModuleRegistry;
private final JSBundleLoader mJSBundleLoader;
private final ArrayList<PendingJSCall> mJSCallsPendingInit = new ArrayList<PendingJSCall>();
private final Object mJSCallsPendingInitLock = new Object();
private ExecutorToken mMainExecutorToken;
private final NativeModuleRegistry mJavaRegistry;
@@ -168,10 +190,20 @@ public void runJSBundle() {
mJSBundleHasLoaded = true;
// incrementPendingJSCalls();
// Loading the bundle is queued on the JS thread, but may not have
// run yet. It's save to set this here, though, since any work it
// gates will be queued on the JS thread behind the load.
mAcceptCalls = true;
synchronized (mJSCallsPendingInitLock) {
// Loading the bundle is queued on the JS thread, but may not have
// run yet. It's save to set this here, though, since any work it
// gates will be queued on the JS thread behind the load.
mAcceptCalls = true;
for (PendingJSCall call : mJSCallsPendingInit) {
callJSFunction(call.mExecutorToken, call.mModule, call.mMethod, call.mArguments);
// This is registered after JS starts since it makes a JS call
@@ -193,7 +225,13 @@ public void callFunction(
if (!mAcceptCalls) {
throw new RuntimeException("Attempt to call JS function before JS bundle is loaded.");
// Most of the time the instance is initialized and we don't need to acquire the lock
synchronized (mJSCallsPendingInitLock) {
if (!mAcceptCalls) {
mJSCallsPendingInit.add(new PendingJSCall(executorToken, module, method, arguments));
callJSFunction(executorToken, module, method, arguments);
@@ -244,11 +282,6 @@ public boolean isDestroyed() {
return mDestroyed;
public boolean isAcceptingCalls() {
return !mDestroyed && mAcceptCalls;
* Initialize all the native modules

0 comments on commit 68aeffe

Please sign in to comment.