1212import javax .annotation .Nullable ;
1313
1414import java .lang .ref .WeakReference ;
15+ import java .util .ArrayList ;
1516import java .util .Collection ;
1617import java .util .concurrent .CopyOnWriteArrayList ;
1718import java .util .concurrent .atomic .AtomicInteger ;
@@ -57,6 +58,25 @@ public class CatalystInstanceImpl implements CatalystInstance {
5758
5859 private static final AtomicInteger sNextInstanceIdForTrace = new AtomicInteger (1 );
5960
61+ private static class PendingJSCall {
62+
63+ public ExecutorToken mExecutorToken ;
64+ public String mModule ;
65+ public String mMethod ;
66+ public NativeArray mArguments ;
67+
68+ public PendingJSCall (
69+ ExecutorToken executorToken ,
70+ String module ,
71+ String method ,
72+ NativeArray arguments ) {
73+ mExecutorToken = executorToken ;
74+ mModule = module ;
75+ mMethod = method ;
76+ mArguments = arguments ;
77+ }
78+ }
79+
6080 // Access from any thread
6181 private final ReactQueueConfigurationImpl mReactQueueConfiguration ;
6282 private final CopyOnWriteArrayList <NotThreadSafeBridgeIdleDebugListener > mBridgeIdleListeners ;
@@ -67,6 +87,8 @@ public class CatalystInstanceImpl implements CatalystInstance {
6787 private final TraceListener mTraceListener ;
6888 private final JavaScriptModuleRegistry mJSModuleRegistry ;
6989 private final JSBundleLoader mJSBundleLoader ;
90+ private final ArrayList <PendingJSCall > mJSCallsPendingInit = new ArrayList <PendingJSCall >();
91+ private final Object mJSCallsPendingInitLock = new Object ();
7092 private ExecutorToken mMainExecutorToken ;
7193
7294 private final NativeModuleRegistry mJavaRegistry ;
@@ -168,10 +190,20 @@ public void runJSBundle() {
168190 mJSBundleHasLoaded = true ;
169191 // incrementPendingJSCalls();
170192 mJSBundleLoader .loadScript (CatalystInstanceImpl .this );
171- // Loading the bundle is queued on the JS thread, but may not have
172- // run yet. It's save to set this here, though, since any work it
173- // gates will be queued on the JS thread behind the load.
174- mAcceptCalls = true ;
193+
194+ synchronized (mJSCallsPendingInitLock ) {
195+ // Loading the bundle is queued on the JS thread, but may not have
196+ // run yet. It's save to set this here, though, since any work it
197+ // gates will be queued on the JS thread behind the load.
198+ mAcceptCalls = true ;
199+
200+ for (PendingJSCall call : mJSCallsPendingInit ) {
201+ callJSFunction (call .mExecutorToken , call .mModule , call .mMethod , call .mArguments );
202+ }
203+ mJSCallsPendingInit .clear ();
204+ }
205+
206+
175207 // This is registered after JS starts since it makes a JS call
176208 Systrace .registerListener (mTraceListener );
177209 }
@@ -193,7 +225,13 @@ public void callFunction(
193225 return ;
194226 }
195227 if (!mAcceptCalls ) {
196- throw new RuntimeException ("Attempt to call JS function before JS bundle is loaded." );
228+ // Most of the time the instance is initialized and we don't need to acquire the lock
229+ synchronized (mJSCallsPendingInitLock ) {
230+ if (!mAcceptCalls ) {
231+ mJSCallsPendingInit .add (new PendingJSCall (executorToken , module , method , arguments ));
232+ return ;
233+ }
234+ }
197235 }
198236
199237 callJSFunction (executorToken , module , method , arguments );
@@ -244,11 +282,6 @@ public boolean isDestroyed() {
244282 return mDestroyed ;
245283 }
246284
247- @ Override
248- public boolean isAcceptingCalls () {
249- return !mDestroyed && mAcceptCalls ;
250- }
251-
252285 /**
253286 * Initialize all the native modules
254287 */
0 commit comments