fix: move model construction off the JS thread#944
fix: move model construction off the JS thread#944msluszniak merged 4 commits intosoftware-mansion:mainfrom
Conversation
1877fe7 to
f293cb2
Compare
|
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. |
|
@msluszniak Fixed! I can't see the CI but pretty sure I made the correct changes. |
|
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 ;) |
|
@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>
|
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
|
Thanks for another contribution @yocontra 🫶, I will perform some quality checks tomorrow and then I think we're ready to merge :) |
chmjkb
left a comment
There was a problem hiding this comment.
Tested the demo apps and it looks good, thank you!
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