0

I have issues with setTimeout() function in Javascript.. I want to display seconds in div with id = time.. But setTimeout() is not working properly because the updation is not done in exactly 1 second.. refresh() is an AJAX request function..

var time = 0;
var url = "";
var tm = 0;
var resp = -1;
function refresh(){ // onload refresh is called();
    if(tm == 0){
        url = "quizload.php";
    }
    else{
       url = "quizload.php?ans=" + resp;
    }
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function(){
        if (this.readyState == 4 && this.status == 200) {
            var a = this.responseText;
            var result = JSON.parse(a);
            document.getElementById("question").innerHTML = result[0];
            document.getElementById("op1").innerHTML = result[1];
            document.getElementById("op2").innerHTML = result[2];
            document.getElementById("op3").innerHTML = result[3];
            document.getElementById("op4").innerHTML = result[4];
            time = result[6] == '1' ? 20 : result[6] == '2' ? 35 : 60; // time = 20
        }
    };
    xhttp.open("GET", url, true);
    xhttp.send();
    tm++;
    if(tm == 11){
        location.href = "result.php";
    }
    update();
}

function update(){
    setTimeout(function(){
        document.getElementById("time").innerHTML = time; // Updation is not exactly 1 second
        time = time - 1; // time is is seconds and value is 20
        if(time == 0){
            refresh(); // Ajax request function
        }
        update();
        },1000);
}

What am I doing wrong here?

Akki Gupta
  • 149
  • 6
  • 2
    Why don't you use `setInterval` instead? – 31piy Jun 13 '18 at 14:27
  • I think your issue could be that time is javascript isn't reliable. See this https://stackoverflow.com/questions/21097421/what-is-the-reason-javascript-settimeout-is-so-inaccurate – spirift Jun 13 '18 at 14:29
  • Please provide the rest of your code. – Miles Grimes Jun 13 '18 at 14:30
  • Akki, you are using update() function recursively with setTimeout() inside of it. Could you simplify your code? – yW0K5o Jun 13 '18 at 14:32
  • @spirift set time in any language is not accurate , you don't dedicated cpu , when set time out you send signal that you go out running and ready queue but you can't choose when to come in – nima moradi Jun 13 '18 at 14:33
  • @31piy I tried that after you pointed it out but still not working.. – Akki Gupta Jun 13 '18 at 14:37
  • Timeout is not accurate. Plenty of questions that talk about it – epascarello Jun 13 '18 at 14:47
  • Possible duplicate of [What is the reason JavaScript setTimeout is so inaccurate?](https://stackoverflow.com/questions/21097421/what-is-the-reason-javascript-settimeout-is-so-inaccurate) – Heretic Monkey Jun 13 '18 at 15:01

2 Answers2

0

This is a common thing with timing in pretty much every language and library. Things will never hit exactly on an even time.

When you use setTimeout() you are really saying "after x-milliseconds have passed, run this function". The catch is, it is doing other things, so what really happens is after x-milliseconds pass, that function is queued up for call, but it may take a few more milliseconds to actually trigger.

In short, you can't create a true clock purely in JavaScript because it'll always drift; usually only by a few milliseconds each tick, but it'll get progressively worse.

let last = Date.now();
let shouldBe = -1000; // account for first tick
let actual = 0;

const run = () => {
  const now = Date.now();
  shouldBe += 1000;
  actual += now - last;
  
  console.log({ sinceLast: now - last, actual, shouldBe, drift: actual - shouldBe });
  
  last = now;
  setTimeout(run, 1000);
};

run();

What you can do however is use setTimeout() at some interval (usually <1 second) at which point you use the Date object (which can read the client's system time) to get the current time and update it that way.

const clock = document.querySelector('div');

const updateClock = () => {
  const now = new Date();
  clock.innerHTML = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
  setTimeout(updateClock, 500);
};

updateClock(); // start it
<div>XX:XX:XX</div>

It'll be off by a few milliseconds each tick, but it'll get back on track and won't be a big deal. This is the only real approach to do this with any level of accuracy.

samanime
  • 25,408
  • 15
  • 90
  • 139
  • Thank you for great explanation... But my timeout issue grows after every call to refresh function from update function.. Do Ajax request have any effect on timeout in above code? – Akki Gupta Jun 13 '18 at 14:55
  • As I demonstrated in my example, it will continually drift and you won't be able to do it with just timeout. You'll need to sync it with time. AJAX requests likely won't affect it much because it is async, but it could affect it slightly. – samanime Jun 13 '18 at 14:59
0

first may be you have to initialize time variable var time = 20 or Date() and after check its i work for me

        var time = 20;
        function update(){
            setTimeout(function(){

                console.log(time); // Updation is not exactly 1 second
                time = time - 1; // time is is seconds and value is 20
                if(time == 0){
                    console.log("print refreash")
                    return false;
                }
                update();
                },1000);
        }
        update()

i got similar error when i try this without initialize var time = 'value' and i get similar as you mentioned

        console.log(time); // Updation is not exactly 1 second
                    ^

ReferenceError: time is not defined
    at Timeout._onTimeout (C:\Users\Manan\Desktop\stack.js:5:21)
    at ontimeout (timers.js:427:11)
    at tryOnTimeout (timers.js:289:5)
    at listOnTimeout (timers.js:252:5)
    at Timer.processTimers (timers.js:212:10)
manan5439
  • 898
  • 9
  • 24
  • Please refer to edited code.. I already defined time and initialized it with 20 as commented in refresh function – Akki Gupta Jun 13 '18 at 15:02
  • Make sure you defined time in globle scope because it is work after initializing and also it didint work after initializing and give error as you mentioned – manan5439 Jun 13 '18 at 15:06