margelo / react-native-worklets-core

🧵 A library to run JS functions ("Worklets") on separate Threads
https://margelo.io
MIT License
578 stars 37 forks source link

Add `worklet()` func that throws if value is not a worklet #187

Closed mrousavy closed 7 months ago

mrousavy commented 7 months ago

Adds the worklet(...) and isWorklet(..) functions:

worklet(...)

const func = worklet(() => {
  'worklet'
  return "hello!"
})
context.runAsync(func)

This will do some runtime checking to ensure the given function is a worklet (uses __initData, __closure and __workletHash) and throws with some useful debugging information if it isn't.

I will use this in VisionCamera's useFrameProcessor function to make sure people pass in worklets (funcs with 'worklet' directive) and they installed the babel plugin properly. Otherwise the error is a bit cryptic, with this it's more clear what went wrong:

- ERROR  Error: The given function ("") is not a Worklet!
- - Make sure react-native-worklets-core is installed properly! 
- - Make sure to add the react-native-worklets-core babel plugin to your babel.config.js! 
- - Make sure that no other plugin overrides the react-native-worklets-core babel plugin! 
- - Make sure the function  is decorated with the 'worklet' directive! 
- Expected  to contain __workletHash and __initData, but  has these properties: 
- 
- This error is located at:
-     in App
-     in RCTView (created by View)
-     in View (created by AppContainer)
-     in RCTView (created by View)
-     in View (created by AppContainer)
-     in AppContainer
-     in WorkletsExample(RootComponent), js engine: hermes

Also, the returned type is useful because you can now safely access func.__workletHash or func.__closure.

isWorklet(...)

Same thing as worklet(...), but only returns false if the function is not a worklet instead of throwing an error.

Changes to useWorklet(..)

The useWorklet hook now uses worklet(..) underneath to ensure the user passed in a correct Worklet.

Also, I now removed the dependencies parameter by automatically using the values in func.__closure for the useMemo's dependencies. This is pretty cool because people often forgot to the hook dependencies in useFrameProcessor and wondered why it didn't update/re-build after a re-render.

mrousavy commented 7 months ago

@chrfalch some questions;

  1. Can we make the babel plugin automatically transpile functions to worklets when they are wrapped in a worklet(...) call? That way users could do:
    const func = worklet(() => {
      return "this is a worklet, without the directive!"
    })

    ...and skip adding the 'worklet' directive in these cases..

  2. Can we remove the legacy Reanimated code branches? The ones with __location and asString and stuff...