#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2015
    Posts
    64
    Rep Power
    4

    help with 2 timers


    Hi!

    I have an application which does the following:
    1) every 1000ms send ajax request to a db and return a result
    2)if the result is not null, call a countdown() function
    3) the countdown function deletes any previous timer and starts a new countdown timer
    4)the ajax call (1) keep running in the background

    Right now the problem is that when I reset the timer inside countdown() the initial setInterval (1) get stuck. I need it instead to be independent and keep running.

    Hope it's clear. Here's my code (I've commented the ajax code so that it's easier to test
    Thanks!

    Code:
    $(document).ready(function() {
        $('#myLog').hide();
    
       //this has to be constantly executed forever
        setInterval(function(){setTimer(0);} , 1000);
    });
    
    function setTimer(){
        canvas = document.getElementById("myCanvas");
        log = document.getElementById("myLog");
        
       //example call
        countdown(5, 0);
    
        //$.ajax ({
        //    url: "ajax.php",
        //    success: function( result ) {
        //       countdown(result, 0);
        //    }
        //});
    
    }
    
    function countdown(minutes, seconds) {
        canvas.style.color = "black";
        canvas.innerHTML = "00:00";
    
       //reset countdown.it works but also affects the initial setInterval and I don't want that
        var id = window.setTimeout(null,0);
        while (id--)
            window.clearTimeout(id);
    
       //from here on all is good
    
        if(minutes == "OFF"){
            canvas.style.color = "#383838";
            canvas.innerHTML = "OFF";
            return;
        }
    
        var endTime, hours, mins, msLeft, time;
    
        function twoDigits( n )
        {
            return (n <= 9 ? "0" + n : n);
        }
    
        function updateTimer()
        {
            canvas.style.color = "black";
                canvas.innerHTML = "00:00"; //clear canvas
                msLeft = endTime - (+new Date);
                if ( msLeft < 1000 )                
                    flashyText();
                else {
                    canvas.style.color = "white";
                    time = new Date( msLeft );
                    hours = time.getUTCHours();
                    mins = time.getUTCMinutes();
                    canvas.innerHTML = (hours ? hours + ':' + twoDigits( mins ) : mins) + ':' + twoDigits( time.getUTCSeconds());
                    setTimeout( updateTimer, time.getUTCMilliseconds() + 500 );
                }
            }
    
        endTime = (+new Date) + 1000 * (60*minutes + seconds) + 500;
        updateTimer();
    }
    
    function flashyText() {
        var count = 1000000,
        timer = setInterval(function() {
            count--;
            if( count%2 == 1) {
                canvas.style.color = "red";
                canvas.innerHTML = "00:00"
            }
            else {
                canvas.style.color = "black";
                canvas.innerHTML = "00:00";
            }
            if( count == 0) clearInterval(timer);
        },1000);
    }
  2. #2
  3. Impoverished Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    16,862
    Rep Power
    9646
    setInterval is generally discouraged, especially when the callback does AJAX. The problem is that the function fires every interval regardless of whether the last call has completed. Sometimes that's a desired effect, but when you're doing AJAX and the request stalls for some reason, adding more every second can easily cause problems.

    Better would be to use setTimeout and then have the function setTimeout itself again.
    javascript Code:
    function setTimer() {
    	// ...
     
    	// $.ajax({
    	//	url: "ajax.php",
    	//	success: function(result) {
    	//		countdown(result, 0);
    	//		window.setTimeout(setTimer, 1000);
    	//	}
    	// });
    }
     
    window.setTimeout(setTimer, 1000);


    You're having that exact problem with countdown: creating a new interval and having to deal with cleaning up existing intervals. The wrong way to do that is to use the returned new identifier as a number and delete all other intervals before it - that can totally conflict with other code running. There should only ever be one interval running so you should only have to disable that one interval.

    Then you have another interval running for flashyText...

    Instead of this deal with creating new intervals and stopping old ones, just have one interval. Ever. The countdown function can start it if it's not already running, otherwise just let it keep going: when the countdown hasn't hit zero you want to update the time, and when it does you want to flash it. Really, the interval only ends once minutes is "OFF", right?

    "But you said it's discouraged!" Yes. Generally discouraged. Some people probably wouldn't use it here either but I would: you want something every second and chaining setTimeouts together will eventually cause a second to be skipped, and the code you're running from the callback won't take too long. So I say an interval here is fine.

    First let's introduce a new scope for this work so it doesn't all happen on the window.
    javascript Code:
    (function() {
    	var interval = 0;
    	var minutes = 0, seconds = 0;
     
    	$(document).ready(function() {
    		...
    	});
     
    	function setTimer() {
    		...
    	}
     
    	function countdown(minutes, seconds) {
    		...
    	}
     
    	function twoDigits(n) {
    		...
    	}
     
    	function updateTimer() {
    		...
    	}
     
    	function flashyText() {
    		...
    	}
    })();

    Oh. Try to avoid defining functions inside of functions. It's not like they're "local" functions (by default) so it's really just a conditional define and that's not great. That's literally what's happening above, however not only is there a technical reason for it (sharing variables) but the outer function isn't running conditionally which was the main problem to be avoided.

    Three "global" variables for all the functions to use except they aren't actually global. Given that there's updateTimer (deal with the countdown) and flashyText (flash the text), the countdown function itself doesn't need to do a whole lot: it can start the interval if it's not yet running, and deal with deciding whether to call updateTimer or flashyText.
    javascript Code:
    function countdown(m, s) { // renamed the parameters to not conflict with the outer variables
    	if (m == "OFF") {
    		if (interval != 0) {
    			window.clearInterval(interval);
    			interval = 0;
    		}
    		clearTimer();
    		return;
    	}
     
    	minutes = m;
    	seconds = s;
    	if (interval == 0) {
    		interval = window.setInterval(function() {
    			if (seconds > 0) {
    				seconds--;
    			} else if (minutes > 0) {
    				seconds = 59;
    				minutes--;
    			}
     
    			if (seconds > 0 || minutes > 0) {
    				updateTimer();
    			} else {
    				flashyText();
    			}
    		}, 1000);
     
    		updateTimer();
    	}
    }
     
    function clearTimer() {
    	// timer has stopped
    	// do whatever you want to clear/reset the display
    }

    Written like this, countdown() is just a way of starting the countdown - the actual logic behind it all is in the other two functions and the anonymous one used by the interval. Which is fine. It also means updateTimer and flashyText can be simplified by each performing their one specific action: update the timer display, or flash the timer display.

    There's also the new clearTimer function to complement them so that stuff hasn't have to go in the countdown function.

    javascript Code:
    function updateTimer() {
    	// look at minutes,seconds and update the display
    	// you can assume the countdown has not reached zero yet
    }
     
    var flashy = 0;
    function flashyText() {
    	flashy = 1 - flashy; // toggles between 0 and 1 each second
    	// you can assume the countdown has reached zero
    	// use flashy to decide how to change the display
    }


    Does that all make sense? It's a bit of a change from what you have now, but I suggest it not just to fix your problem but to clean up the code a bit too.
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2015
    Posts
    64
    Rep Power
    4
    Hi, thanks for the super detailed answer. However no, it's not really clear within your code, where should I put the ajax code? Also I don't understand how this code deals with the ajax requests being sent every second while the other countdown (either normal or flashy) are running..
    Thanks!
  6. #4
  7. Impoverished Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    16,862
    Rep Power
    9646
    The AJAX goes in the same place as before: setTimer.

    setTimer will call itself so all you have to do is call it the first time to start the process. It has the AJAX for the timer data, and when that request comes back the callback calls the countdown function and then sets a timeout for the next setTimer to kick in. Meanwhile countdown will use an interval to do the actual counting down.
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2015
    Posts
    64
    Rep Power
    4
    Hi, I still don't get it..the setTimer is never called again..where should I call it in order to repeat it every 1 second?
    What am I supposed to do with the updateTimer(), clearTimer() and flashyText? Because my initial updateTimer() had the setTimeout inside, but now that is completely different I have no clue on what to do inside that..
    Thanks
    Last edited by balux; February 7th, 2018 at 02:46 PM.
  10. #6
  11. Impoverished Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    16,862
    Rep Power
    9646
    setTimer will call itself (kinda). Please read what I posted:
    javascript Code:
    function setTimer() {
    	// ...
     
    	// $.ajax({
    	//	url: "ajax.php",
    	//	success: function(result) {
    	//		countdown(result, 0);
    	//		window.setTimeout(setTimer, 1000);
    	//	}
    	// });
    }
     
    window.setTimeout(setTimer, 1000);

    See those parts that are commented out? You're supposed to uncomment them once you have ajax.php working. See the part in there that calls window.setTimeout?

    - updateTimer updates the timer display according to how many minutes and seconds are left. Just the display.
    - clearTimer "clears" the timer so it doesn't look like it's running. Just the display.
    - flashyText flashes the timer every time it's called by changing colors and whatnot. Just the display.
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2015
    Posts
    64
    Rep Power
    4
    Hi, sorry I was confused and thought that first part was wrong..I almost have it working..I'm just not sure where to put the setTimeout() call. Right now I have it both in updateTimer() and flashyText and it works. However it the
    Code:
    m == "OFF"
    condition (inside countdown) is true it will stop working. I need the
    Code:
    window.setTimeout(setTimer(), 1000);
    to keep running always

    Code:
    (function() {
    	var interval = 0;
    	var minutes = 0, seconds = 0;
    
    	$(document).ready(function() {
    		canvas = document.getElementById("myCanvas");
    		log = document.getElementById("myLog");
    		setTimer();
    	});
    
    	function setTimer() {
    		//console.log("setTimer");
    		$.ajax ({
    			url: "ajax.php",
    			success: function( result ) {
    				countdown(result, 0);
    			}
    		});
    	}
    
    	function countdown(m, s) {
    
    		if (m == "OFF") {
    			if (interval != 0) {
    				window.clearInterval(interval);
    				interval = 0;
    			}
    			clearTimer();
    			return;
    		}
    
    		minutes = m;
    		seconds = s+1;
    
    		if (interval == 0) {
    			interval = window.setInterval(function() {
    				if (seconds > 0) {
    					seconds--;
    				} else if (minutes > 0) {
    					seconds = 59;
    					minutes--;
    				}
    
    				if (seconds > 0 || minutes > 0) {
    					updateTimer(minutes, seconds);
    				} else {
    					flashyText();
    				}
    			}, 1000);
    
    			updateTimer(minutes, seconds);
    		}
    	}
    
    	function twoDigits(n) {
    		return (n <= 9 ? "0" + n : n);
    	}
    
    	function updateTimer(minutes, seconds) {
    		window.setTimeout(setTimer, 1000);
    		canvas.style.color = "#fff";
    		canvas.innerHTML = minutes + ':' + twoDigits(seconds);
    	}
    
    	function clearTimer(){
    		canvas.style.color = "#000";
    		canvas.innerHTML = "00:00";
    	}
    
    	var flashy = 0;
    	function flashyText() {
    		window.setTimeout(setTimer, 1000);
    		canvas.style.color = flashy ? "red" : "#000";
    		canvas.innerHTML = "00:00";
    		flashy = 1 - flashy;
    	}
    
    })();
    Last edited by balux; February 10th, 2018 at 06:22 PM.
  14. #8
  15. Impoverished Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    16,862
    Rep Power
    9646
    Okay, you're completely ignoring everything I've said.

    If you have code in a condition and don't want it to run according to the condition then remove the condition.
  16. #9
  17. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2015
    Posts
    64
    Rep Power
    4
    Originally Posted by requinix
    Okay, you're completely ignoring everything I've said.
    I'm not..I've just posted some code and you replied with a completely different solution and I'm trying to understand it.

    If you have code in a condition and don't want it to run according to the condition then remove the condition.
    This means nothing except you don't feel like answering. That's fine, you don't have to.

    Anyway, maybe it wasn't clear but
    Code:
    m=="OFF"
    doesn't mean "turn off everything". It just means to clear the html code inside the canvas element. The timer for the ajax request should keep going and sending requests.
    If I move
    Code:
     if (interval != 0) {
    			window.clearInterval(interval);
    			interval = 0;
    		}
    outside of the if condition it won't work anyway.
    If I put the
    Code:
    window.setTimeout(setTimer, 1000);
    right after/outside setTimer definition (as you did), it won't work

IMN logo majestic logo threadwatch logo seochat tools logo