Worklets/Threading
Nitro itself is fully runtime-agnostic, which means every Hybrid Object can be used from any JS Runtime or Worklet Context.
This allows the caller to call into native Nitro Modules from libraries like react-native-worklets-core, or react-native-reanimated. You can use a Nitro Hybrid Object on the default React JS context, on the UI context, or on any other background worklet context.
- Worklets Core
- Reanimated
const math = NitroModules.createHybridObject<Math>('Math')
const boxed = NitroModules.box(math)
const context = Worklets.createContext('DummyContext')
context.runAsync(() => {
'worklet'
const unboxed = boxed.unbox()
console.log(unboxed.add(5, 3)) // --> 8
})
const math = NitroModules.createHybridObject<Math>('Math')
const boxed = NitroModules.box(math)
runOnUI(() => {
'worklet'
const unboxed = boxed.unbox()
console.log(unboxed.add(5, 3)) // --> 8
})()
Boxing
Since Nitro uses newer JSI APIs like jsi::NativeState
- which current worklet libraries (like react-native-worklets-core or react-native-reanimated) do not yet fully support - Hybrid Objects cannot yet be directly used in worklet contexts - they have to be boxed.
A boxed Hybrid Object is a native jsi::HostObject
, which is supported by worklet libraries. The process is as following:
- In the runtime your
HybridObject
was created in (probably the default runtime), callNitroModules.box(...)
to box it. - The boxed result can be shared in any (worklet-)runtime if needed.
- To use the original
HybridObject
, simply call.unbox()
on it in the desired (worklet-)runtime. - The result of
.unbox()
is the originalHybridObject
- you can now call any methods on it as usual.
In future versions of react-native-worklets-core or react-native-reanimated we expect fullly automatic jsi::NativeState
support, which will make boxing obsolete.
Dispatcher
All synchronous APIs of Nitro work ✨ automagically ✨ on any runtime, but asynchronous APIs (Promises and callbacks) require a Dispatcher
.
If you call an asynchronous API on a runtime that Nitro doesn't know, it likely doesn't have a Dispatcher
, so it doesn't know how to call back to the JS Thread after the asynchronous operation has finished (Promise resolve or callback call).
If you created that jsi::Runtime
, you need to create a Dispatcher
for it and implement runSync
and runAsync
:
#include <NitroModules/Dispatcher.hpp>
using namespace margelo::nitro;
class MyRuntimeDispatcher: public Dispatcher {
public:
void runSync(std::function<void()>&& function) override;
void runAsync(std::function<void()>&& function) override;
};
Then, simply install this Dispatcher
into your runtime so Nitro can use it:
auto myDispatcher = std::make_shared<MyRuntimeDispatcher>();
Dispatcher::installRuntimeGlobalDispatcher(myRuntime, myDispatcher);
This needs to be done once, ideally immediately as soon as possible after creating the jsi::Runtime
.
Your runSync
and runAsync
implementations must run the given function
on the same Thread that the jsi::Runtime
was created on - see CallInvokerDispatcher.hpp
for an example.