Skip to content

iOS: callAsyncJavaScript module method for reply-in-one-hop calls - #19

Draft
artemlitch wants to merge 2 commits into
masterfrom
artem/portal-gate-fast-path
Draft

artemlitch wants to merge 2 commits into
masterfrom
artem/portal-gate-fast-path

Conversation

@artemlitch

Copy link
Copy Markdown
Collaborator

Shelved for later. Paired with a Rekindled draft PR that uses it.

Problem

Bookwise talks to the reader WebView through the portal gate. An RN-to-WebView call takes two hops. RN injects a script with injectJavaScript. The page then answers with postMessage, which reaches RN as a Fabric onMessage event.

Change

RNCWebViewModule.callAsyncJavaScript(viewTag, functionBody, argument) runs a function in the page through WebKit's callAsyncJavaScript and resolves the TurboModule promise with the string it returns. The reply comes back with the call, so there is no postMessage and no onMessage event.

  • RNCWebView keeps a weak tag-to-view registry so the module can find the view.
  • The module method runs on the main queue.
  • The WebView ref gets callAsyncJavaScript(functionBody, argument), which finds the tag for you.
  • Android only gets a stub that rejects, so the spec still compiles there.

Measurements

These are from a Release build of Bookwise on the iPhone 17 Pro simulator. Both columns already have the Rekindled log-batching fix (readwiseio/rekindled#13526), so only the transport differs.

Scenario onMessage reply callAsyncJavaScript
Empty round trip, median 0.12 ms 0.075 ms
Small gate call, median 0.16 ms 0.11 ms
50 KB gate call, median 0.91 ms 0.51 ms
200 concurrent gate calls, total 22 ms 13 ms

I also tried a dedicated WebKit message handler that delivered page-to-RN messages through a TurboModule event emitter. It was slower than onMessage, 8.5 ms against 7.4 ms for 200 events, so I removed it.

Before merging

  • A page reload mid-call rejects with a WebKit error. Callers can't tell that apart from a disconnect.
  • Not measured on a device.

🤖 Generated with Claude Code

https://claude.ai/code/session_014HGrJetPUopRfPnfocezrH

artemlitch and others added 2 commits October 17, 2026 03:00
…ge handler

callAsyncJavaScript(viewTag, functionBody, argument) runs a function in the page and
resolves with its string result, so a caller gets a reply without a postMessage and
onMessage round trip. The ReactNativeWebViewFast handler delivers page messages through
the module's onFastMessage emitter; it measured slower than onMessage and is unused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It measured slower than onMessage, so only the callAsyncJavaScript call path remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014HGrJetPUopRfPnfocezrH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant