Skip to content

Commit

Permalink
Better error message when trying to call non-worklet from UI thread (#…
Browse files Browse the repository at this point in the history
…3821)

## Summary

After #3722 the error message presented after attempting to
synchronously call React runtime function from the UI runtime has
changed and became less descriptive than before. This PR adds a way for
us to throw a more descriptive error in such scenario.

## Test plan

Use the following code:
```
function rnMethod() {
  console.log('from RN');
}

function uiMethod() {
  'worklet';
  rnMethod(); // this should throw
}

runOnUI(uiMethod)();
```

Before this change you'd get a generic "Object is not a function" thrown
at the line where we call `rnMethod`, now the error message says "Tried
to synchronously call a non-worklet function" and presents possible
solution (see the diff for the whole error message)

Co-authored-by: Tomek Zawadzki <tomasz.zawadzki@swmansion.com>
  • Loading branch information
kmagiera and tomekzaw authored Nov 30, 2022
1 parent ef6e0f2 commit ecf719a
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 1 deletion.
7 changes: 7 additions & 0 deletions Common/cpp/SharedItems/Shareables.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,14 @@ class ShareableRemoteFunction
function_(std::move(function)) {}
jsi::Value toJSValue(jsi::Runtime &rt) override {
if (runtimeHelper_->isUIRuntime(rt)) {
#ifdef DEBUG
return runtimeHelper_->valueUnpacker->call(
rt,
ShareableJSRef::newHostObject(rt, shared_from_this()),
jsi::String::createFromAscii(rt, "RemoteFunction"));
#else
return ShareableJSRef::newHostObject(rt, shared_from_this());
#endif
} else {
return jsi::Value(rt, function_);
}
Expand Down
1 change: 1 addition & 0 deletions src/reanimated2/commonTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export interface NativeEvent<T> {
export interface ComplexWorkletFunction<A extends any[], R>
extends WorkletFunction {
(...args: A): R;
__remoteFunction?: (...args: A) => R;
}

export interface NestedObject<T> {
Expand Down
12 changes: 11 additions & 1 deletion src/reanimated2/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export function getViewProp<T>(viewTag: string, propName: string): Promise<T> {
});
}

function valueUnpacker(objectToUnpack: any): any {
function valueUnpacker(objectToUnpack: any, category?: string): any {
'worklet';
let workletsCache = global.__workletsCache;
let handleCache = global.__handleCache;
Expand Down Expand Up @@ -142,6 +142,16 @@ function valueUnpacker(objectToUnpack: any): any {
handleCache!.set(objectToUnpack, value);
}
return value;
} else if (category === 'RemoteFunction') {
const fun = () => {
throw new Error(`Tried to synchronously call a non-worklet function on the UI thread.
Possible solutions are:
a) If you want to synchronously execute this method, mark it as a worklet
b) If you want to execute this function on the JS thread, wrap it using \`runOnJS\``);
};
fun.__remoteFunction = objectToUnpack;
return fun;
} else {
throw new Error('data type not recognized by unpack method');
}
Expand Down
7 changes: 7 additions & 0 deletions src/reanimated2/threads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ export function runOnJS<A extends any[], R>(
fun: ComplexWorkletFunction<A, R>
): (...args: A) => void {
'worklet';
if (fun.__remoteFunction) {
// in development mode the function provided as `fun` throws an error message
// such that when someone accidently calls it directly on the UI runtime, they
// see that they should use `runOnJS` instead. To facilitate that we purt the
// reference to the original remote function in the `__remoteFunction` property.
fun = fun.__remoteFunction;
}
if (!_WORKLET) {
return fun;
}
Expand Down

0 comments on commit ecf719a

Please sign in to comment.