Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript setTimeout unexpected output

I've been working with scripts making use of setInterval(fn,delay) function in my application and after reading about how setTimeout and JS work I encounter some weird results so I made a test: Here is the jsfiddle https://jsfiddle.net/jyq46uu1/2/

And the code as suggested:

var limit        = 1000000000;
var intervals    = 0;
var totalTime    = new Date();
var startTime    = new Date();

var uid = setInterval(
    function () {
        // final lap?
        if (intervals == 9) clearInterval(uid);

        for (i = 0; i < limit; i += 1) {
            // just working the CPU
        }
        // reduce the iterations
        limit = limit / 10;   
        intervals += 1;        

        console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime));
        // reset the time
        startTime    = new Date();
    }, 250, 9);

Ok, from what I've red from http://ejohn.org/blog/how-javascript-timers-work/ Javascript make the timer calls for the function in setInterval even if the "thread is blocked", so if the function is still executing the call is just queued and so on and so on ... in my notebook that code produces this:

"Interval 1 Time elapsed    : 4264" 
"Interval 2 Time elapsed    : 477" 
"Interval 3 Time elapsed    : 91" 
"Interval 4 Time elapsed    : 170" 
"Interval 5 Time elapsed    : 246" 
"Interval 6 Time elapsed    : 242" 
"Interval 7 Time elapsed    : 248" 
"Interval 8 Time elapsed    : 248" 
"Interval 9 Time elapsed    : 248"

Ok, if what I've red is true, by the time the first interval finished, ALL functions calls were in the queue ... in my script I'm reducing the work for every execution so every call should take fewer seconds than the previous one, BUT no matter how much iterations I set, the elapsed time always pick up the Interval pace after the 4th run. Maybe I got it wrong but if by the time 4264 all functions are already in the queue and are suppose to run immediately, they should show less time, right? ... if the 3th iteration displays 91 and the others are just behind they should take 91 or less. But this is not the case.

If you understand what is happening please explain it to me because I think I'm missing something.

like image 718
Gabriel Matusevich Avatar asked Jul 08 '15 04:07

Gabriel Matusevich


1 Answers

I think the first time is not counting from the initial interval in the queue, but from the point where you set startTime.

Then the first timer is counting initialization time, environment creation, variables and timers allocation.

Try this modification:

var limit        = 1000000000;
var intervals    = 0;
var totalTime    = new Date();
var startTime    = false;

var uid = setInterval(
    function () {
        if (!startTime) startTime = new Date();
        // final lap?
        if (intervals == 9) clearInterval(uid);

        for (i = 0; i < limit; i += 1) {
            // just working the CPU
        }
        // reduce the iterations
        limit = limit / 10;   
        intervals += 1;        

        console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime));
        // reset the time
        startTime    = new Date();
    }, 250, 9);

Also, the first time takes longer probably because the function is being interpreted, then the subsequent times is being executed "compiled"/optimized by the Javascript JIT ( see https://en.wikipedia.org/wiki/Just-in-time_compilation ). Check out this code to see the proof

var limit        = 1000000000;
var intervals    = 0;
var totalTime    = new Date();
var startTime    = new Date();

var uid = setInterval(
    function () {
        startTime = new Date();
        // final lap?
        if (intervals == 9) clearInterval(uid);

        for (i = 0; i < limit; i += 1) {
            // just working the CPU
        }
        // reduce the iterations
        limit = limit / 10;   
        intervals += 1;        

        console.log('Interval ' + intervals +' Time elapsed : ' + (new Date() - startTime));
        // reset the time
        startTime    = new Date();
    }, 250, 9);

the output:

Interval 1 Time elapsed : 3249
Interval 2 Time elapsed : 299
Interval 3 Time elapsed : 31
Interval 4 Time elapsed : 5
Interval 5 Time elapsed : 0
Interval 6 Time elapsed : 0
Interval 7 Time elapsed : 0
Interval 8 Time elapsed : 0
Interval 9 Time elapsed : 0
Interval 10 Time elapsed : 0

setInterval is not good measuring the time in which it should queue the next execution. I think this has to do with sleep()ing. It is not reliable if a process is taking the cpu and hanging it (like the for you do).

setInterval guarantees only this:

  • Execution number 1 is not going to happen in less than 250ms.
  • Execution number 2 is not going to happen in less than 2*250ms ahead.
  • Execution number 3 is not going to happen in less than 3*250ms ahead.
  • ...

setInterval does not guarantee that some execution is going to happen before a determined time in the future. That depends on the current CPU usage.

The queuing and launching of queued functions depends also on current CPU usage.

Extra Common Advice If you need to execute a function periodically, I recommend this approach that guarantees a delay always greather than 250ms between executions

var t = false;
var fun = function() {
    console.log("run");
    t = setTimeout(fun, 250);
}
fun();
like image 186
jperelli Avatar answered Oct 20 '22 11:10

jperelli