2

I have a setInterval function, which displays the remaining time for an event on my website. But the countdown is not in sync with the actual tick of the second.

My code uses an ajax call to the server to get the expiry date once, and on its success the countdown will start. Great till there.

var request = new XMLHttpRequest();
request.open('GET', 'https://my-website/service.php', true);
request.onload = function() {
    if (request.status >= 200 && request.status < 400) {

        date = request.responseText;
        timer = setInterval(showRemaining, 1000);//start the countdown

    } else {
        // We reached our target server, but it returned an error
    }
};

But the time when setInterval is called needs to be in sync with actual global tick of the second.

(I hope I make sense. I mean the calls need to be in sync with each time a second passes in your PC's or phone's clock!)

How can I achieve that? Thanks in advance!

freedomn-m
  • 27,664
  • 8
  • 35
  • 57
Shah Abaz Khan
  • 565
  • 4
  • 21
  • 1
    You need to do an initial `setTimeout` with the difference between the current ms and the next ms (ie `1000-(new Date().getMilliseconds())`) then start the `setInterval` Note that setTimeout has a minimum value, so if it's less than that value to the next second, add 1000. – freedomn-m Jul 17 '17 at 08:07
  • https://stackoverflow.com/a/9647221/2181514 – freedomn-m Jul 17 '17 at 08:09
  • what if the clock of pc/mobile is different, like a week in the future? here are some advices how to find the offset between your server clock and the local clock https://stackoverflow.com/questions/1638337/the-best-way-to-synchronize-client-side-javascript-clock-with-server-date – john Smith Jul 17 '17 at 08:16
  • As @johnSmith rightly states, you are using server-side response date, so you can't use my suggested method of getMilliseconds (client-side time). – freedomn-m Jul 17 '17 at 08:18
  • @johnSmith Hi, PC and Mobile doesn't have anything to do with my question! I was making "tick of the second" clear :) – Shah Abaz Khan Jul 17 '17 at 08:34
  • Just to make sure, you're only getting the date from the server once, right? You get it once, and then count down to that date without making any more requests. – MySidesTheyAreGone Jul 17 '17 at 08:39
  • @MySidesTheyAreGone That is correct. – Shah Abaz Khan Jul 17 '17 at 08:47
  • @freedomn-m Thank you! Your solution worked. I'll post it as an answer myself if i can! – Shah Abaz Khan Jul 17 '17 at 08:48

2 Answers2

2

You need to make an initial setTimeout with the difference between the current ms and the next ms, ie:

1000-(new Date().getMilliseconds())) 

then you can start the setInterval

Note that setTimeout/setInterval has a minimum value (generally considered 10ms), so if it's less than that value to the next second, add 1000.

Also note that setTimeout/setInterval are not 100% accurate, but for the nearest second will likely suffice.

This gives your success code:

 date = request.responseText; 

 var t = 1000-(new Date().getMilliseconds());
 if (t < 15) t+=1000;

 setTimeout(function() {
     timer = setInterval(showRemaining, 1000);//start the countdown
 }, t));
freedomn-m
  • 27,664
  • 8
  • 35
  • 57
  • This is correct because `setInterval` appears to not drift - it looks like [it corrects itself](https://stackoverflow.com/questions/985670/will-setinterval-drift). I learned something too :) – MySidesTheyAreGone Jul 17 '17 at 12:24
0

As @freedomn-m suggested in the comments, 1000-(new Date().getMilliseconds()) is the key piece of code I was looking for - the difference between the current ms and the next ms. So my code is now working and it looks like this:

if (request.status >= 200 && request.status < 400) {

    date = request.responseText;
    setTimeout(function() {
        timer = setInterval(showRemaining, 1000);//start the countdown
    }, 1000-(new Date().getMilliseconds()));//to make the calls in sync with actual tick of the second

}
Shah Abaz Khan
  • 565
  • 4
  • 21