react-native-reanimated
react-native-reanimated copied to clipboard
Make worklets, runOnUI and runOnJS awaitable
Description
Makes worklets (runOnUI
) and runOnJS
return a Promise so it can be awaited and can now return values.
const result = await runOnUI(() => {
return 'Test!'
})();
console.log(result);
Changes
- Makes worklets (
runOnUI
) return a Promise - Makes hostFunction (
runOnJS
) return a Promise
Test code and steps to reproduce
Test await:
const run = async () => {
console.log('in run')
const x = await runOnUI(() => {
'worklet';
return 'result from worklet'
})()
console.log(`runOnUI result: ${x}`);
};
run();
Test errors thrown being Promise.rejected:
try {
const result = await runOnUI(() => {
'worklet';
throw new Error("Failed!")
})();
} catch (e) {
console.error(`Error: ${e.message}`)
}
Test awaiting runOnJS in a worklet:
const run = async () => {
console.log('in run')
const x = await runOnUI(async () => {
'worklet';
console.log('in runOnUI')
const res = await runOnJS(doSomethingOnJS)();
console.log(`runOnJS result: ${res}`)
return 'yooo'
})()
console.log(`runOnUI result: ${x}`);
};
run();
⚠️ warning: the above snippet still crashes because the babel plugin requires additional transformation steps so it can successfully transform async/await code. Right now it just submits the above code with the
await
statement as is, and that crashes theWorkletsCache
eval code because you cannot use top-level await there. I'm waiting on @piaskowyk / @Szymon20000 's feedback here.
Checklist
- [x] Included code example that can be used to test this change
- [x] Updated TS types
- [ ] Added TS types tests
- [ ] Added unit / integration tests
- [ ] Updated documentation
- [x] Ensured that CI passes
Missing
When calling from JS -> UI it successfully returns a Promise which now can be awaited. When calling from UI -> UI, it does not return a Promise, but rather returns the return value of the worklet as is: https://github.com/software-mansion/react-native-reanimated/blob/afa2791ed3c95de7c0c2d869104994a5362f0928/Common/cpp/SharedItems/ShareableValue.cpp#L354
I tried to use Promise.resolve(res)
here, but this always gave me crashes because apparently some code depends on this behaviour, so I just left it as it is right now. This will likely never affect the user, as he never calls runOnUI(() => runOnUI...)()
.
I've also managed to make runOnJS
awaitable, but you cannot create worklets with async functions. So the following code:
const run = async () => {
console.log('in run')
const x = await runOnUI(async () => {
'worklet';
console.log('in runOnUI')
const res = await runOnJS(doSomethingOnJS)();
console.log(`runOnJS result: ${res}`)
return 'yooo'
})()
console.log(`runOnUI result: ${x}`);
};
run();
crashes because the eval()
func in WorkletsCache
cannot accept an async function with an await statement, but this code:
const run = async () => {
console.log('in run')
const x = await runOnUI(async () => {
'worklet';
console.log('in runOnUI')
runOnJS(doSomethingOnJS)().then((res) => {
console.log(`runOnJS result: ${res}`)
});
return 'yooo'
})()
console.log(`runOnUI result: ${x}`);
};
run();
works perfectly fine. We can update the types so runOnUI
does not accept async functions, and users can use .then
and .catch
to "await" results. This is not ideal, but it works for now. Alternatively, we can adjust the babel plugin to transform async/await code so it can be evaluated by WorkletsCache
, and the user can use await
inside of worklets.
What do you think @Szymon20000 @piaskowyk ?
Oh maybe that async
worklet kept crashing because Hermes doesn't support async/await yet? 🤔
@mrousavy by using this spawnThread
is not showing console.log
but showing native log
+ console.log
complete block + runOnJS
cannot call api
Ah, it took me a while to realize async function cannot be put inside runOnJS
.
I was calling a mutateAsync
function from react-query
to make a post request when swiping a card left or right in tinder like fashion.
Fortunately in this case I could just switch to a synchronous mutate function.
Either way, this PR from @mrousavy looks like it would be a nice addition.
After the Shareable
s rewrite recently merged into v3, this would also need to be rewritten from scratch.
It's also not obvious how the inclusion of this feature fits into Reanimated.
If you're still interested in this, feel free to open a new PR with an updated implementation.