fix: move model construction off the JS thread#944
Open
yocontra wants to merge 4 commits intosoftware-mansion:mainfrom
Open
fix: move model construction off the JS thread#944yocontra wants to merge 4 commits intosoftware-mansion:mainfrom
yocontra wants to merge 4 commits intosoftware-mansion:mainfrom
Conversation
1877fe7 to
f293cb2
Compare
Member
|
There is an error in CI build, specifically in VerticalOCR, I guess we just need to mark it as promise like for the other models. |
Contributor
Author
|
@msluszniak Fixed! I can't see the CI but pretty sure I made the correct changes. |
Member
|
Cool now all important checks work. Now, we just need to fix lint, I will fix this for you later today and then proceed with the actual review ;) |
Contributor
Author
|
@msluszniak Fixed the lint errors - my bad |
The `loadModel` template in `RnExecutorchInstaller.h` constructs model objects synchronously on the JS thread. For models like Kokoro TTS, the constructor loads .pte files, initializes the phonemizer, and reads voice data — blocking the JS thread for several seconds. This prevents React from rendering loading states (spinners, progress indicators) until construction completes. This change makes `loadModel` return a Promise and dispatches the model construction to `GlobalThreadPool::detach`, matching the pattern already used by `promiseHostFunction` for inference calls like `generate()`. On the JS side, all `global.load*()` call sites are updated to `await` the now-async result, and the global type declarations are updated to return `Promise<any>`. This is a breaking change for any consumers calling `global.load*()` directly and expecting a synchronous return value. The public module APIs (`load()` methods) are already async, so no user-facing API changes are needed.
The `await` keyword was used in loadNativeModule without the method being marked async, causing a Babel parse error (UnexpectedReservedWord).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Member
|
FYI @yocontra, I will push changes to the API reference so CI will pass. Other then that, I tested changes and everything was correct. :)) |
ff67697 to
32620fd
Compare
Collaborator
|
Thanks for another contribution @yocontra 🫶, I will perform some quality checks tomorrow and then I think we're ready to merge :) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
loadModeltemplate inRnExecutorchInstaller.hconstructs model objects synchronously on the JS thread. For models like Kokoro TTS, the constructor loads.ptefiles, initializes the phonemizer, and reads voice data — blocking the JS thread for several seconds.This prevents React from rendering any pending state updates (loading spinners, progress indicators, etc.) until construction completes. For example, calling
setIsSynthesizing(true)followed bytts.load()will not show the loading UI for 3-4 seconds because the JS thread is blocked by the synchronous native constructor.Solution
Make
loadModelreturn a Promise and dispatch the heavy model construction toGlobalThreadPool::detach— matching the pattern already used bypromiseHostFunctionfor inference calls likegenerate().C++ (
RnExecutorchInstaller.h)jsi::Valueaccess)GlobalThreadPool::detachcallInvoker->invokeAsyncwith the host objectpromiseHostFunctioninModelHostObject.hTypeScript
global.load*()call sites updated toawaitthe now-async resultPromise<any>SpeechToTextalready usedawait, so no change needed thereBreaking Change
This is a breaking change for any consumers calling
global.load*()directly and expecting a synchronous return value. However:load()methods) are alreadyasync— no user-facing API changes neededglobal.load*()functions are internal/undocumentedTest Plan
load()is called, rather than being delayed until construction completes