setTimeout not working after hours of research. - javascript

I have been looking for answers for hours and it seems that my problem is with closure. I could not figure out how to fix it in my scenario. I want to trigger an animation onload on 6 elements, all 1000ms apart. I can't get the setTimeout method to work properly. Please help!
$(document).ready(function() {
we();
});
var data = ['450px', '300px', '200px', '120px', '250px', '320px'];
var bars = $('.bar');
var i = 0;
function grow(size, elem) {
$(elem).animate({
height: size,
opacity: '1.0'
}, 1000);
}
function we() {
setTimeout (function(){ // if I don't use an annonymous function call here, they all animate at the same time
grow(data[i], bars[i]);
}, 1000);
if (i >= bars.length) {
return;
} else {
i++;
return we();
};
}

You have to put your setTimeout around the rest of your function as well; not just the call to grow.
function we() {
setTimeout (function(){
grow(data[i], bars[i]);
if (i >= bars.length) {
return;
} else {
i++;
return we();
};
}, 1000);
}
I think what you've missed is that setTimeout does not block your program. It doesn't stop execution, wait for 1000 ms and then continue. Instead, it will delay the execution of the anonymous function for 1000 ms, but the rest of the program will continue to execute right away. And that part of your program will call we once again, and schedule a new animation (almost at the same time as the last one). And then another and so on.
By putting the recursive call inside setTimeout, we delay the recursion as well and hence all future animation steps.

Related

Function with repeated setTimeout() changes on each call

I am trying to set up intervals with setTimeout() and it works the first time the code is executed. The second time (after a new interval is created), the setTimeouts (that are pushed to intervalTimeouts) are somehow delayed. The code before the setTimeout (rep--; and so on) is instead executed twice, then the setTimeout is repeated twice, simultaneously. The third time the function is called, the code before the setTimeout repeats three times before the timeouts start. I tried removing the delay before the function calls itself (by changing setTimeout(intervalRep, pause * 1000); to intervalRep();) but this does not solve the problem. Does anyone have a clue as to why this is happening?
function intervalRep() {
if (intervalsRunning === true) {
rep--;
document.body.style.background = '#FB2843';
calcSprintSegments(sprint * 1000);
longBeep.play();
intervalTimeouts.push(setTimeout(function() {
if (rep > 0) {
document.body.style.background = '#BB39F0';
lowBeep.play();
setTimeout(intervalRep, pause * 1000);
} else {
intervalsRunning = false;
intervalTimeouts.forEach(function(t) {
clearTimeout(t);
});
intervalTimeouts = [];
document.body.style.background = '#4A3DF9';
cheering.play();
}
}, sprint * 1000)
);
}
}

Javascript asynchronous for wait

I'm trying to run an asynchronous for loop, which runs a synchronous function and then waits for a period of time. My code is :
function loopExec (i,imax, execFunc,param1) {
execFunc(i,param1);//Launch synchronous function which takes some time
var y=i+1;
if (y < imax) { // if the counter < imax, call the loop function
setTimeout(function () { // call a 0.1s setTimeout when the loop is called
loopExec(y,imax, execFunc,param1); // .. again which will trigger another call
}, 100);
}
else if(y==imax){
anotherFunction(param1);// The loop is over, clean up and log
}
}
The behaviour that I want is :
execFunc(1) -> wait 100ms -> execFunc2-> wait...
A bit like this
The behaviour that I have is execFunc1, execFunc2, etc all launching at 100 ms interval, without waiting for completion of the previous one, resulting in a read/write conflict as these functions interact with files.
I don't know if this has anything to do with it, but I'm using electron/nodeJS.
T.J. Crowder was right, the error is not in this function, but in the function it was calling.
This code does what is expected.
If you want to do something like this
this code will works.
var i = 0;
var imax = 10;
function myFunction() {
console.log('balbla', i);
i+=1;
if(i !== imax) setTimeout(myFunction, 100);
}
setTimeout(myFunction, 100);

How to create delay function in QML?

I would like to create a delay function in javascript that takes a parameter of amount of time to delay, so that I could use it do introduce delay between execution of JavaScript lines in my QML application. It would perhaps look like this:
function delay(delayTime) {
// code to create delay
}
I need the body of the function delay(). Note that setTimeout() of JavaScript doesn't work in QML.
As suggested in the comments to your question, the Timer component is a good solution to this.
function Timer() {
return Qt.createQmlObject("import QtQuick 2.0; Timer {}", root);
}
timer = new Timer();
timer.interval = 1000;
timer.repeat = true;
timer.triggered.connect(function () {
print("I'm triggered once every second");
})
timer.start();
The above would be how I'm currently using it, and here's how I might have implemented the example in your question.
function delay(delayTime) {
timer = new Timer();
timer.interval = delayTime;
timer.repeat = false;
timer.start();
}
(Which doesn't do anything; read on)
Though the exact way you are looking for it to be implemented suggests that you are looking for it to block until the next line of your program executes. But this isn't a very good way to go about it as it would also block everything else in your program as JavaScript only runs in a single thread of execution.
An alternative is to pass a callback.
function delay(delayTime, cb) {
timer = new Timer();
timer.interval = delayTime;
timer.repeat = false;
timer.triggered.connect(cb);
timer.start();
}
Which would allow you to use it as such.
delay(1000, function() {
print("I am called one second after I was started.");
});
Hope it helps!
Edit: The above assumes you're working in a separate JavaScript file that you later import into your QML file. To do the equivalent in a QML file directly, you can do this.
import QtQuick 2.0
Rectangle {
width: 800
height: 600
color: "brown"
Timer {
id: timer
}
function delay(delayTime, cb) {
timer.interval = delayTime;
timer.repeat = false;
timer.triggered.connect(cb);
timer.start();
}
Rectangle {
id: rectangle
color: "yellow"
anchors.fill: parent
anchors.margins: 100
opacity: 0
Behavior on opacity {
NumberAnimation {
duration: 500
}
}
}
Component.onCompleted: {
print("I'm printed right away..")
delay(1000, function() {
print("And I'm printed after 1 second!")
rectangle.opacity = 1
})
}
}
I'm not convinced that this is the solution to your actual problem however; to delay an animation, you could use PauseAnimation.
Marcus' answer does the job, but there is one big problem.
The problem is that the callback keeps connected to triggered signal even after triggered once. This means that if you use that delay function again, the timer will triggers all callbacks connected before again. So you should disconnect the callback after triggered.
This is my enhanced version of the delay function:
Timer {
id: timer
function setTimeout(cb, delayTime) {
timer.interval = delayTime;
timer.repeat = false;
timer.triggered.connect(cb);
timer.triggered.connect(function release () {
timer.triggered.disconnect(cb); // This is important
timer.triggered.disconnect(release); // This is important as well
});
timer.start();
}
}
...
timer.setTimeout(function(){ console.log("triggered"); }, 1000);
Here's another variation which utilizes the Component object to house the Timer object.
Then we implement a setTimeout look-a-like function to dynamically create and invoke this Timer object.
N.B. The answer assumes Qt5.12.x which includes ECMAScript 7 (and therefore ECMAScript 6) to utilize parameter shortcuts, rest parameters and spread syntax:
function setTimeout(func, interval, ...params) {
return setTimeoutComponent.createObject(app, { func, interval, params} );
}
function clearTimeout(timerObj) {
timerObj.stop();
timerObj.destroy();
}
Component {
id: setTimeoutComponent
Timer {
property var func
property var params
running: true
repeat: false
onTriggered: {
func(...params);
destroy();
}
}
}
In the following snippet, we will invoke console.log(31), console.log(32), console.log(33) in a random time delay between 0-1000ms from now.
console.log("Started");
setTimeout(console.log, Math.floor(1000 * Math.random()), 31);
setTimeout(console.log, Math.floor(1000 * Math.random()), 32);
setTimeout(console.log, Math.floor(1000 * Math.random()), 33);
See also: https://community.esri.com/groups/appstudio/blog/2019/05/22/ecmascript-7-settimeout-and-arrow-functions
The answer from Bumsik Kim is great, this answer changes it slightly so that the timer can be used on a repeating basis and then stopped and reused when desired.
The QML for the timer to add where required.
// Allow outside access (optional)
property alias timer: timer
Timer {
id: timer
// Start the timer and execute the provided callback on every X milliseconds
function startTimer(callback, milliseconds) {
timer.interval = milliseconds;
timer.repeat = true;
timer.triggered.connect(callback);
timer.start();
}
// Stop the timer and unregister the callback
function stopTimer(callback) {
timer.stop();
timer.triggered.disconnect(callback);
}
}
This can be used as follows.
timer.startTimer(Foo, 1000); // Run Foo every 1 second
timer.stopTimer(Foo); // Stop running Foo
timer.startTimer(Bar, 2000); // Run Bar every 2 seconds
timer.stopTimer(Bar); // Stop running Bar
function Foo() {
console.log('Executed Foo');
}
function Bar() {
console.log('Executed Bar');
}
Here's my continued evolution of the prior answers https://stackoverflow.com/a/62051450/3220983 and https://stackoverflow.com/a/50224584/3220983...
Add this file / component to your project:
Scheduler.qml
import QtQuick 2.0
Timer {
id: timer
property var _cbFunc: null
property int _asyncTimeout: 250
// Execute the callback asynchonously (ommiting a specific delay time)
function async( cbFunc )
{ delay( cbFunc, _asyncTimeout ) }
// Start the timer and execute the provided callback ONCE after X ms
function delay( cbFunc, milliseconds )
{ _start( cbFunc, milliseconds, false ) }
// Start the timer and execute the provided callback repeatedly every X ms
function periodic( cbFunc, milliseconds )
{ _start( cbFunc, milliseconds, true ) }
function _start( cbFunc, milliseconds, isRepeat ) {
if( cbFunc === null ) return
cancel()
_cbFunc = cbFunc
timer.interval = milliseconds
timer.repeat = isRepeat
timer.triggered.connect( cbFunc )
timer.start()
}
// Stop the timer and unregister the cbFunc
function cancel() {
if( _cbFunc === null ) return
timer.stop()
timer.triggered.disconnect( _cbFunc )
_cbFunc = null
}
}
Then, implement in another component like:
...
Scheduler { id: scheduler; }
scheduler.delay( function(){ console.log('Delayed'); }, 3000 );
You can use anonymous functions like shown here, or else callback into named functions. Use the simple async to fire off code in a non-blocking manner, if you aren't too concerned about the exact timing. Note that while it's tempting to use a 0 ms timeout for an "asynchronous" callback (as one would with a C++ QTimer), that is not the right approach with a QML Timer! These timers don't appear to queue events on an event loop where screen redraws are given priority. So, if your goal is to defer a given operation to achieve "instant" UI changes first, you need to dial up the delay interval as shown here. Using 0ms will often cause the code to fire prior to redraws.
Note that one of these "Scheduler" instances can only be bound to one callback function, on one given interval, at a time. Multiple instances are required if you need to "overlap" delayed events.
you can use QtTest
import QtTest 1.0
import QtQuick 2.9
ApplicationWindow{
id: window
TestEvent {
id: test
}
function delay_ms(delay_time) {
test.mouseClick(window, 0, 0, Qt.NoButton, Qt.NoModifier, delay_time)
}
}
This should be sufficient:
void QmlUtils::singleShot(int msec, QJSValue callback)
{
QTimer::singleShot(msec, this, [callback] () mutable {
if (callback.isCallable())
callback.call();
});
}
then call it in QML, wherever you are:
qmlUtils.singleShot(5000, () => console.log("Hello!"))
Done.
If you want, you can use this without even writing it. Simply expose it to QML with:
ctx->setContextProperty("lqtUtils", new lqt::QmlUtils(qApp));

"too much recursion" in my rotating banner script

My rotating banner script is throwing a "Too much recursion" error when the iKeyless.widget.Rotator.rotate(); function is called within init();. I can't figure out why, init(); is only called once and there's a defer() on the second call.... when the page loads it lags before throwing "too much recursion"
return {
init: function () {
ui.container = $("#rotating-banner");
ui.thumbs = $("#rotating-banner .tabs .tab");
ui.banners = $("#rotating-banner .banner");
ui.fadeBox = $(".rotate .bd .fade-box");
ui.thumbs.each(function (idx, el) {
$(el).click(function () {
paused = true;
iKeyless.widget.Rotator.rotate.show(idx);
});
});
iKeyless.widget.Rotator.rotate();
},
show: function (idx) {
ui.fadeBox.css("display", "block");
ui.fadeBox.animate({
opacity: 1
},
.125,
function () {
$('.active', $(ui.banners[idx]).addClass('active').parent()).removeClass('active');
ui.fadeBox.animate({
opacity: 1
},
.125,
function () {
ui.fadeBox.css("display", "none");
});
});
},
rotate: function () {
if (!paused) {
this.show(counter);
counter = counter + 1;
if (counter === ui.thumbs.length) {
counter = 0;
}
iKeyless.widget.Rotator.rotate().defer(5000);
}
}
}
I think the issue is that you need to do this
iKeyless.widget.Rotator.rotate.defer(5000);
instead of this
iKeyless.widget.Rotator.rotate().defer(5000);
Source: When you write rotate(), you execute the function rotate and then execute defer. When you write rotate.defer() you are getting the function rotate, and using the method defer that is defined on Function.
Also, if this is jQuery, how do you have defer defined? I'm not sure jQuery provides a defer function. If this is Prototype, I don't think defer takes any arguments. Perhaps you want delay. Delay takes a time in seconds, so you'd want (assuming 5 seconds, not 5000 seconds):
iKeyless.widget.Rotator.rotate.delay(5);
I didn't know the defer, but what I read is this:
Schedules the function to run as soon as the interpreter is idle.
But that doesn't mean that it doesn't actually do it. The function rotate calls itself, again and again. Not linear, but inside itself, so you'll never end the 'top' calling rotate.
This would mean infinite recursion, and so I'm not surprised at that error.
I'm not familiar with defer() but you're calling rotate() inside your rotate method which is where the recursion is coming from. Perhaps leveraging something like setTimeout or setInterval is a way to work around your issue?

Javascript functions through the url bar with a delay

So.. I have a webpage with a javascript function I wish to execute..
Not knowing javascript very well I exectue the function through the url bar..
javascript: Myfunct1();
javascript: Myfunct2();
Now what I really need to be able to do is a long sleep, execute the first function, sleep for a little, then execute the second function, then loop forever.. something like:
javascript: while(1) { Sleep(20000); Myfunct1(); Sleep(5000); Myfunct2() };
Obviously there isn't a 'Sleep' function.. and this is my problem.. After looking at various posts about 'setTimeout;, I tried that but have been unable to get it right.. was wondering if somebody would take pitty and a poor javascript simpleton and show me the way to do this?
have a look at setTimeout(). This will give you the delay you're looking for.
http://www.w3schools.com/js/js_timing.asp
Just pop this into your HTML before the </body> tag
<script type="text/javascript"><!--
setTimeout(function(){
Myfunct1();
setTimeout(function(){
Myfunct2();
},5000);
},20000);
--></script>
You can use setInterval in conjuction with setTimeout function:
setTimeout('Myfunct1(); setInterval("Myfunct1();", 25000);', 20000);
setTimeout('Myfunct2(); setInterval("Myfunct2();", 25000);', 25000);
This will accomplish the functionality like in your example without hanging the browser. Basicallly, it will Myfunct1() after 20s, and set it to run again 25s after that. Same thing is with Myfunct2(), except that it first run after 25s.
Here's a function that allows you to call alternating functions, waiting a specified amount of time between each invocation:
function alt(fn1, tm1, fn2, tm2) {
var curr, time;
(function next() {
curr = (curr === fn1) ? fn2 : fn1;
time = (time === tm1) ? tm2 : tm1;
window.setTimeout(function() {
curr();
next();
}, time);
})();
}
Use it like this:
alt(Myfunct1, 20000,
Myfunct2, 5000);
This will wait 20 seconds, then call Myfunct1, then wait 5 seconds and call Myfunct2, then wait 20 seconds and call Myfunct1 again, and so on.
Here's a general purpose version that accepts any number of function/timeout pairs:
function alt() {
var args = arguments;
(function next(i) {
if (i == args.length)
i = 0;
window.setTimeout(function() {
args[i]();
next(i + 2);
}, args[i + 1]);
})(0);
}
It's used the same way, but can accept more than two pairs:
alt(function(){console.log("1")}, 2000,
function(){console.log("2")}, 1000,
function(){console.log("3")}, 5000);
If this were real code there's a lot more you could do, like verify arguments and/or specify default timeouts when not provided for any of the given functions.

Categories

Resources