I am trying to break the setTimeout function which is started on page load. So what I am doing here is, If I click on the button then I making flag value to true and setTimeout should break which isn't happening here.
This setTimeout function is inside the for each loop. Below is my code.
rData[0].dt.forEach(function(d, i) {
setTimeout(function() {
if(flag === "false"){
console.log(flag);
reserRadius(i); //here I am changing radius of circle
}else{
console.log(flag);
clearTimeout();
return;
}
}, i * 2000);
});
Instead of creating all timeouts in one go, only create them when needed. This way you don't have to clear any of them when you have determined to stop:
(function repeat(list, i) {
if (i >= list.length) return; // nothing (more) to do
var d = list[i]; // do you need d at all??
setTimeout(function() {
if(flag === "false"){
console.log(flag);
reserRadius(i); //here I am changing radius of circle
repeat(list, i+1); // schedule next timeout only now.
}else{
console.log(flag);
// Don't schedule next timeout. This breaks the "loop".
}
}, 2000); // trigger 2 seconds from now. Note: no multiplying anymore.
})(rData[0].dt, 0); // pass initial values: the array and index.
In your version of the code, you would have to keep the id values returned by all setTimeout calls, and then pass them all (or at the least the remaining ones) to clearTimeout, one by one. This would make your code quite cumbersome. I think the above is a more efficient approach.
setTimeout cannot be stopped from its callback itself. setTimeout
returns a timeoutId which can be passed to clearTimeout which in turn will
stop that particualr timer.
One way to stop all such timers is to create an array of timeoutIds and make changes as following.
var timerIds = [];
rData[0].dt.forEach(function(d, i) {
timerIds.push(setTimeout(function(){
if(flag === "false"){
console.log(flag);
reserRadius(i); //here I am changing radius of circle
}
else{
console.log(flag);
}
}, i * 2000));
});
function stopTimeouts(){
timerIds.forEach(function(id){
clearTimeout(id);
}
}
function codeThatMightChangeFlag(callback) {
// do a bunch of stuff
if (condition happens to change flag value) {
// call the callback to notify other code
stopTimeouts();
}
}
Refer: Clear array of setTimeout's and Javascript - wait until flag=true
Related
I currently try to get into JavaScript a bit. (vanilla)
So at the moment what i want to do is swap different element nodes.
As an example imagine a list with different entries, which later shall be swapped.
For this for element.length times random elements should be swapped. After each of this swaps should be a timeout.
The problem which i now encounter is that the list only updates after the shuffle function finishes.
It is intended to swap a pair, wait for 1 second and then do the next swap.
function shuffle(element){
disableButtons();
let clicked_elmnt = document.getElementsByTagName(element);
console.log(clicked_elmnt);
if(clicked_elmnt.length !== 1 && clicked_elmnt !== 0){
for (let i = 0; i<clicked_elmnt.length;++i){
setTimeout(function(){doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length))}, 1000)
}
}
enableButtons();
}
This code should check if there is a list in a list and then roll new numbers if it tries to swap a parent with its child. (Seems to be working)
function doIt(element,first,second){
let clicked_elmnt = document.getElementsByTagName(element);
if(clicked_elmnt[first].children.length > 0){
if(clicked_elmnt[first].firstElementChild.isSameNode(clicked_elmnt[second].parentNode)){
doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length));
}else{
if(clicked_elmnt[second].children.length > 0){
if(clicked_elmnt[second].firstElementChild.isSameNode(clicked_elmnt[first].parentNode)){
doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length));
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}
}else{
if(clicked_elmnt[second].children.length > 0){
if(clicked_elmnt[second].firstElementChild.isSameNode(clicked_elmnt[first].parentNode)){
doIt(element,randomNumber(clicked_elmnt.length),randomNumber(clicked_elmnt.length));
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}else{
doSwap(clicked_elmnt[first],clicked_elmnt[second]);
}
}
}
And the swap then happens in the next function. Here they are swapped.
function doSwap(n1, n2){
console.log("swap");
const afterN2 = n2.nextElementSibling;
const parent = n2.parentNode;
if (n1 === afterN2) {
parent.insertBefore(n1, n2);
} else {
n1.replaceWith(n2);
parent.insertBefore(n1, afterN2);
}
}
Hope someone can help me here without that much weird stuff.
Cheers!
My understanding is you wish that the doIt calls, the callbacks of setTimeout to be 1 second apart from each other. The way setTimeout works is the callbacks are queued to be executed x milliseconds after setTimeout is called. If you want one function to be called 1 second after the previous function finishes, you could call another setTimeout from inside the callback.
Here's your example adjusted as such - the doItLater function includes a setTimeout call, and will recursively call itself clicked_elmnt.length times. You may be able to set it up more cleanly for a real scenario, but this fits without deviating too much from your example.
function shuffle(element) {
disableButtons();
let clicked_elmnt = document.getElementsByTagName(element);
if (clicked_elmnt.length !== 1 && clicked_elmnt !== 0) {
const doItLater = function (i) {
setTimeout(() => {
doIt(element, randomNumber(clicked_elmnt.length), randomNumber(clicked_elmnt.length));
if (i + 1 < clicked_elmnt.length) {
doItLater(i + 1);
}
}, 1000);
}
doItLater(0);
}
enableButtons();
}
If you want to only call enableButtons after the last doIt call finishes, you could move that call to the else clause of if (i + 1 < clicked_elmnt.length).
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);
I have a function in JavaScript. I use setInterval in order to control my function. I also have another logic for controlling my function. I have a counter which is increased once one condition happens and is decreased when another condition happens. Now, sometimes the second condition does not happen and hence my function won't be resume anymore. (I pause my function when my first condition happen). Therefore, I want to wait at most 30 seconds for the second condition. If it does not happen, then I want to resume my function anyway. I have following code, but it does not work as I expect it. What happens is that it resume my function every 30 seconds. Then, it may be resumed while it should wait. Can someone let me know what is the problem with my code?
Please note that, the value for the counter may increase to more than 20. I mean the first and second condition may occur more than once.
function main()
{
// body
}
function increaseCounter()
{
counter += 1;
clearInterval(controller);
controlSecond = setInterval(function(){
counterSeconds += 1;
if (counterSeconds == 30)
{
counterSeconds = 0;
controller = setInterval(main, 100);
clearInterval(controlSecond);
}
}, 1000);
}
function decreaseCounter()
{
counter -= 1;
if (counter == 0)
{
counterSeconds = 0;
clearInterval(controlSecond);
controller = setInterval(main, 100);
}
}
Consider what happens if you call increaseCounter twice in a row.
On the first execution it will create interval A and assign it to controlSecond.
On the second execution it will create interval B and assign it to controlSecond, while interval A continues to fire off indefinitely. You won't stop it with clearInterval(controlSecond) because controlSecond no longer references interval A.
The problem is that you continue to set controlSecond and controller to a new interval without clearing them first. That results in the intervals being leaked with no way of clearing them. It's sort of like a memory leak where you have dynamically allocated memory but nothing pointed at it, but instead of renegade memory you have renegade intervals.
One way to prevent this is to make sure you always clear your interval before setting it.
I would also recommend that you implement controlSecond with a setTimeout because that is designed for tasks which only happen once.
Why not
var counter = 0
var timeout = null
function main () {
clearTimeout(timeout);
timeout = null;
}
function increaseCounter () {
counter++;
if (!timeout)
timeout = setTimeout(main, 30*1000);
}
function decreaseCounter() {
counter--;
if (counter === 0)
main();
}
I need to invoke some function given number of times through given delays. How should I do - declare variable for timer and pass it to invoking function for stopping timer in some moment or in loop (n times) invoke setTimeout once ( or some another approach to skeep delay time once) or other.Thanks.
edit to fix syntax eror
var timerID = null;
var n = 5;
this.timerID = setInterval(function(){
funcToInvoke(n,timerID){
if(invokeNumber == n){
clearInterval(timerID);
return;
}
else { do something}
}
},delay)
Yes, the approach is common and better than calling setTimeout in a loop (with a fixed number of times). It is more performant than that and also more flexible, because the interval will be stopped dynamically (might check for a future condition).
However, your code is a bit messy. Fixed:
// Assuming we a have
// n
// delay
// funcToInvoke
// and execute in context of some object
var that = this,
numberOfInvokes = 0;
this.timer = setInterval(function() {
// "this" points to the global object
if (numberOfInvokes == n)
clearInterval(that.timer);
else
funcToInvoke(numberOfInvokes);
numberOfInvokes++;
}, delay);
Your current method has a syntax problem, you can't have a function parameter like this.timerID). In fact, you should remove the whole funcToInvoke declaration, and declare n and timerID as local variables, so they will be available to the closure. Like this:
// Don't forget to define n here!
var n = 5;
// Change timerID to local var instead of property
var timerID = null;
timerID = setInterval(function(){
if(invokeNumber == n){
clearInterval(timerID);
return;
} else {
//do something
}
// You can setTimeout again anywhere in this function if needed
}, delay);
If you want an approximate delay, setInterval is probably ok. If you want a more precise interval, then repeated calls to setTimeout are better as you can adjust the length of time to the next call based on the time since the last call.
E.g. for a clock ticking every second, you can do repeated calls to setTimeout, setting the lag to just after the next full second.
I am working on a way to autocomplete function to navigate through steps of a form. Here is the code that when 5 characters are entered into an input, it then moves to the next element. My delay is working great, but I don't have a way to stop it from completing if characters get deleted after 5 characters are entered. It just fires off the focus right after that no matter what has changed in the input.
Any thoughts?
var delay = (function(){
var timer = 0;
return function(callback, ms) {
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
$('input').keyup(function(){
if($(this).val().length == 5) {
delay(function(){ $("#saveForm2").focus(); }, 2000 );
}
})
If you're looking for an easy way to associate a timeout instance with an element, consider using jQuery's .data() method.
Something like this.
$('input').keyup(function() {
var $th = $(this);
var data = $th.data();
if(data.timeout === undefined) {
data.timeout = null;
}
if ($th.val().length == 5) {
clearTimeout(data.timeout);
data.timeout = setTimeout(function() {
$("#saveForm2").focus();
}, 2000);
} else {
clearTimeout(data.timeout);
}
});
I don't think the way you were using the closure was quite right. I think you would need to assign the handler to the element inside the closure as well, so it has a local reference to the instance.
EDIT: Made a little more efficient with earlier stored reference to data().
Each time you call delay(), you clobber your timeout handle, which means that you can't manage it after the fact. It also means that you're going to fire off a request every time you hit 5 characters, if I read that correctly. Try something like this:
var delayTimer;
var nextField = function() { $("#saveForm2").focus(); }
$('input').keyup(function(){
clearTimeout(delayTimer);
if($(this).val().length >= 5) {
delayTimer = setTimeout(nextField, 2000);
}
})
That'll a) fire off no more than 1 request unless you wait more than 2 seconds between keystrokes, and b) will cancel any pending request if you drop back under 5 characters before the timeout expires. As a bonus, it won't create a whole mess of anonymous functions.