Skip to main content

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.

const math = NitroModules.createHybridObject<Math>('Math')
runOnUI(() => {
'worklet'
const result = math.add(5, 3)
console.log(result) // --> 8
})()

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 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.

Boxing

A Hybrid Object is a JS object with jsi::NativeState and a prototype chain. If you need to interop with legacy APIs or APIs that can't deal with jsi::NativeState yet, you can box the Hybrid Object into a jsi::HostObject:

const math = NitroModules.createHybridObject<Math>('Math')
const boxed = NitroModules.box(math) // <-- jsi::HostObject

The boxed object is a simple jsi::HostObject (see BoxedHybridObject.hpp), which can later be unboxed again:

const unboxed = boxed.unbox()    // <-- Math
const result = unboxed.add(5, 3) // <-- 8
info

This is how Hybrid Objects are captured inside Worklet Contexts under the hood as well!