How a call reaches native code
A call goes from your JavaScript through a proxy and JSI to your C++. Synchronous calls stay on the JS thread; async ones run on the Lucent thread.
A synchronous call
Section titled “A synchronous call”- The proxy hands the call to the
LucentTurboModule, through JSI. - Argument conversion checks each value against its parameter's type, then copies it into C++.
- Your C++ runs on the JS thread, while JavaScript waits for it.
- Return conversion turns the C++ result into a JavaScript value.
A wrong value never reaches your code. It throws a TypeError that names the function and the argument:
TypeError: greet: argument 'name' must be a string, got a numberAn async call
Section titled “An async call”export async function mean(values: number[]): Promise<number> { let sum = 0; for (const value of values) sum += value; return values.length > 0 ? sum / values.length : 0;}- The arguments are checked and copied on the JS thread, so a wrong value throws at once rather than rejecting.
- The body runs on the Lucent thread, a background thread, from its first line.
- The result is posted back to the JS thread, converted, and resolves the promise. A thrown error rejects it instead.
All Lucent code runs under one lock, one piece at a time, like JavaScript. A synchronous call waits while async Lucent code runs, until that code reaches an await.
What crosses the boundary
Section titled “What crosses the boundary”Numbers, strings, arrays and objects are copied. Class instances cross by reference and keep their identity, functions become callbacks, and promises stay promises. Type conversions has every type.