Tried to synchronously call function {w} from a different thread - React Native Reanimated - react-native

I have a problem with runOnJS in my swipe function.
All the time Im gets error:
java.lang.RuntimeException: Tried to synchronously call function {w} from a different thread.
Im gets the error in panGesture function when its called finishAnimation.
Code is here:
https://pastebin.com/YaQs4bN6

You're calling "finishAnimation" from the onEnd callback. That could be a problem, since finishAnimation isn't a worklet.
So you have two options:
finishAnimation can be marked with the "worklet" keyword
const finishAnimation = (swipe_down) => {
"worklet";
// This logger can't be here anymore since it's a JS function
// Logger.bool(swipe_down, { swipe_down });
if (swipe_down) {
offset.value = withTiming(height.value, { duration: 100 }, () =>
runOnJS(props.onSwipeComplete)()
);
} else {
offset.value = withTiming(0, { duration: 200 });
}
};
finishAnimation can be called async on the JS Thread:
runOnJS(finishAnimation)(
e.velocityY > swipeOutVelocity || offset.value > calculateThreshold()
);
Hopefully it's going to work.

As you are using Reanimated 2. you can add the Worklet directive to onSwipeComplete then you can run this function in both UI and JavaScript` threads.
More about worklet here - https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/worklets

Related

change setInterval to requestAnimationFrame

How do I change the following code? I did not know how to convert this code. Can anyone help me?
let timer = ref(30);
let interval = setInterval(() => {
if (timer.value === 0) {
clearInterval(interval)
alert('done')
} else {
timer.value--
}
}, 1000)
This should work the same way as your original code. Of course, you don't need to pass an interval since requestAnimationFrame will be timed to the screen refresh rate.
let timer = ref(30);
function mainLoop() {
if (timer.value === 0) {
alert('done')
} else {
timer.value--
}
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
First the function mainLoop is defined. Then, it is called through requestAnimationFrame at the end of the script to 'kickstart' the loop. The subsequent calls to requestAnimationFrame within the function are there to maintain it.

undefined is not an object (evaluating 'fun.__callAsync') trying to use setTimeout in a component

I'm using React Native and Reanimated and I want an animation to play after 2 seconds.
When a user moves a card, the card should stay at it's new position for 2 seconds, and then move back to it's place.
This is what I have:
const panGesture = useAnimatedGestureHandler<PanGestureHandlerGestureEvent>({
onActive: event => {
translateX.value = event.translationX;
if (event.translationX <= 0) {
// disabling swiping from right to left
translateX.value = 0;
}
},
onEnd: event => {
const shouldStick = translateX.value >= TRANSLATE_X_THRESHOULD;
if (shouldStick) {
translateX.value = withTiming(120);
runOnJS(moveBack)(translateX);
} else {
translateX.value = withTiming(0);
}
},
});
I tried using setTimeOut to count 2 seconds, and then update translateX but I get this error:
undefined is not an object (evaluating 'fun.__callAsync')
This is moveBack function:
const moveBack = (translateX: Animated.SharedValue<number>) => {
console.log("TRANSLATEX: " + translateX);
setTimeout(() => {
translateX.value = 0;
}, 2000);
}
I don't even see the TRANSLATEX log, so I guess it won't even get there.
I can't really figure out what's the problem or how to word it so I can find a solution.
The solution was way easier than I thought.
I'm using Reanimated 2.2.0 and there is withDelay option to add to the animation and it works great.
This is what I added after translateX.value = withTiming(120); (instead of the runOnJs line):
translateX.value = withDelay(2000, withTiming(0));
So right after setting translateX to 120, it waits 2 seconds, and then setting the value back to 0.
when using runOnJS
const doSomething = () => {
...
} // declare arrow function before runOnJS
runOnJS(doSomething)(arg)
function doSomething(){
...
} // can declare normal function before/after runOnJS coz of hoisting

How to render text only after a Promise has been resolved in React Native?

I am trying to dynamically translate some text to be displayed when a user clicks on the translate button, but I can't get it to save my values outside of the Promise. I haven't worked much with Promises and every example only shows console.log, rather than saving values outside of the Promise. I don't really understand how they work. Here is (most of) the code I am trying to fix:
constructor(props) {
super(props);
this.state = {
dynamicTranslate: this.props.dynamicTranslate,
};
}
// I've tried this method as both sync and async (with await) but neither work
googleTranslate = (key) => {
const translator = TranslatorFactory.createTranslator();
// translate returns a Promise
return translator.translate(key, i18n.locale)
.then((response) => {return response});
}
renderText() {
// getting some values....
// this loops through all the feedback information
for (var i = 0; i < components_feedback.length; i++) {
let label = (some string);
let value = (some string);
// to do: call google translate call here if Boolean(this.state.dynamicTranslate)
if (Boolean(this.state.dynamicTranslate)) {
// I am ultimately trying to save the translation string from googleTranslate()
// in label/value so I can push it into feedbacks
label = this.googleTranslate(label);
value = this.googleTranslate(value);
}
feedbacks.push({label: label, value: value, type: comp.type})
}
return (
// some stuff
feedbacks.map((feedback, index)) => {
// some stuff
<Text>{feedback.label}</Text>
<Text>{feedback.value}</Text>
// some other stuff
});
);
}
render() {
return (
<View>{this.renderText()}</View>
);
}
One of the issues I'm running into is that label/value is a Promise if translation is on. If I try to make renderText() an async method, it is also turned into a Promise which render() can't handle. No idea where to go from here.
Solved this issue. Solution is to put the loop in an async function that (ideally) gets called on construction. This loop was edited to await the returns and push to local arrays of labels and values then saves those in state. You can compare the length of those arrays to the expected length (compare length of last array being used to be positive that it has finished) and that is how you can know if the Promises have returned. Paraphrased code:
constructor(props) {
this.state = {
translatedLabels = []
translatedValues = []
}
this.asyncFunction()
}
asyncFunction = () => {
labels = []
for loop
label = await promise
labels.push(label)
//same for values
end for
this.setState({translatedLabels: labels})
}
//later
renderText() {
if (this.state.translatedLabels.length === whatever) {
// do your stuff as you know the async function has finished
}
}
render() {
return (
{this.renderText()}
);
}

start countdown without any events

I have this script for countdown I wanted it to start as soon as the page loads (ideally i wanted it to run continuously since it is repeating in intervals).
when I call any function such as [startCountdown()] or [ countdown.start($('#countdown_clock').val());] I keep getting the same error [Uncaught ReferenceError: countdown is not defined]
here is the whole function
window.onload = function mainCountdown() {
var countdown = Tock({
countdown: true,
interval: 250,
callback: function () {
// console.log(countdown.lap() / 1000);
$('#countdown_clock').val(countdown.msToTime(countdown.lap()));
// countdown.start($('#countdown_clock').val());
},
complete: function () {
// console.log('end');
// alert("Time's up!");
repeatCountdown();
console.log('alarm');
}
});
$('#startCountdown').on('click', function () {
countdown.start($('#countdown_clock').val());
});
$('#pauseCountdown').on('click', function () {
countdown.pause();
});
$('#stopCountdown').on('click', function () {
countdown.stop();
});
$('#resetCountdown').on('click', function () {
countdown.stop();
$('#countdown_clock').val('00:10');
});
function repeatCountdown() {
countdown.stop();
$('#countdown_clock').val('00:10');
countdown.start($('#countdown_clock').val());
}
function startCountdown(){
countdown.start($('#countdown_clock').val());
}
}
How can I start the countdown without any button events.
Thank you in advance
You seem to have a few syntax issues that might be causing the problem. First, I'd call the window load event like this:
$(window).load(function () {
// functions, etc here
});
Then, inside the window load event, you can create your timer like this:
var countdown = new Tock(...
You were missing the 'var' and 'new' keywords. You might want to revisit the Tock documentation to make sure everything else was correct, too.

Complete function in WinJS.UI.Pages.IPageControlMembers method

How can I complete function in init before call ready method. My code:
WinJS.Namespace.define("Data", {
source: ""
});
var page = WinJS.UI.Pages.define("/html/page.html", {
init: function (element, options) {
createDataSoucre();
},
ready: function () {
document.getElementById("result").innerHTML = Data.source;
}
});
function createDataSoucre() {
//blah blah (calculate thousands of calculations)
Data.source = result;
}
When I run, page doesn't render "result" tag. I try use promises but it doesn't work for me:
init: function (element, options) {
return new WinJS.Promise.as(createDataSoucre());
}
Thanks for your time.
I tried your code in a simple test project as follows:
(function () {
"use strict";
WinJS.Namespace.define("Data", {
source: ""
});
function createDataSource() {
Data.source = "<ul><li>Item 1</li><li>Item2</li><li>Item3</li></ul>";
}
WinJS.UI.Pages.define("/pages/home/home.html", {
init: function (element, options) {
createDataSource();
},
ready: function (element, options) {
document.getElementById("result").innerHTML = Data.source;
}
});
})();
Everything works as expected, with the bullet list appearing on the page.
However, I believe you're asking how to make your createDataSource function asynchronous in itself, so that it can do your "thousands of calculations" off the UI thread and produce a promise that the init function can return. This way, the page loading process will wait upon the completion of those calculations.
What's needed here is to use new WinJS.Promise rather than WinJS.Promise.as. The as method just wraps a value in a promise so that the value is send to any completed handler you attach, but doesn't create an async function automatically. That's something you have to do.
Let me provide an example from Appendix A of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, where Chapter 3 and Appendix A go into all the details about promises. Here's a function that does a long series of calculations using setImmediate to break up the work on the UI thread. (You could also use web workers to put the work on another thread, or use a WinRT component--but I'll leave it to my book to talk about those subjects, which is in Chapter 18 in the section "Implementing Asynchronous Methods").
function calculateIntegerSum(max, step) {
//The WinJS.Promise constructor's argument is a function that receives
//dispatchers for completed, error, and progress cases.
return new WinJS.Promise(function (completeDispatch, errorDispatch, progressDispatch) {
var sum = 0;
function iterate(args) {
for (var i = args.start; i < args.end; i++) {
sum += i;
};
if (i >= max) {
//Complete--dispatch results to completed handlers
Data.source = "Sum is <em>" + sum + "</em>";
completeDispatch(sum);
} else {
//Dispatch intermediate results to progress handlers
progressDispatch(sum);
setImmediate(iterate, { start: args.end, end: Math.min(args.end + step, max) });
}
}
setImmediate(iterate, { start: 0, end: Math.min(step, max) });
});
}
You can do something similar for your own process. The key here is that when your process is complete, you have to call the completeDispatch function that's given to your initializer. In the code above I'm also putting the result into Data.source.
With such a method, your createDataSource function can look like this:
function createDataSource() {
return calculateIntegerSum(100000, 2);
}
Because calculateIntegerSum returns a promise and is implemented to be async, createDataSource will return a promise that you can return from init:
init: function (element, options) {
return createDataSource();
},
I tried this out in a project and it works just fine, with the page loading waiting upon the calculations to complete.