0

Consider the following javascript algorithm:

for(var i = 0; i < 50; i ++){
    console.log('starting :', i);
    getPrimes(1000000);
    (function(i){
        setTimeout(function(){
            console.log('done :', i);
        }, 1000);
    })(i);
}

function getPrimes(max) {
    var sieve = [], i, j, primes = [];
    for (i = 2; i <= max; ++i) {
        if (!sieve[i]) {
            // i has not been marked -- it is prime
            primes.push(i);
            for (j = i << 1; j <= max; j += i) {
                sieve[j] = true;
            }
        }
    }
    return primes;
}

The for loop launches getPrimes function that takes some time and then runs another function that has timeout of one second. Thinking about this algorithm what I will be expecting to get on the console is a mix between "starting" and "done" lines ordered from start to end but not necessarily sorted from 1 to 50.

In reality what I get is 50 lines of "starting" (that take some time to process) and then immidiatelly 50 lines of "done".

My question is why? The logic tells that while running the for loop at least one timout callback should be finished and because javascript is single threaded asynchronous it should display a line of "done" in between some lines of "starting"

Could it be because the primes calculation takes all the cpu power?

Thanks

Vova Lando
  • 558
  • 3
  • 15
Dimkin
  • 670
  • 2
  • 9
  • 22
  • `Could it be because the primes calculation takes all the cpu power?` Just look for it in the task manager. – ˈvɔlə May 15 '14 at 10:57
  • ´setTimeout´ does not mean "run after x ms", but rather "run after x ms, but only if the cpu is ready". Javascript will never run a setTimeout in the middle of a synchronous code block. – Henrik Karlsson May 15 '14 at 11:00

3 Answers3

3

The problem lies within javascrypt events array, so all the timeout functions runs are getting into array, and javascript virtual machine runs them after for loop finishes his run. For better understanding ypu can run this code:

for( var i=0; i<10; i++) {
  console.log(i);
  setTimeout(function(){
       console.log('hhh');
  },0)
}

So the output will be all the before setTimeout console.logs and then all the setTimeout's console.log.

When we call setTimeout, a timeout event is queued. Then execution continues: the line after the setTimeout call runs, and then the line after that, and so on, until there are no lines left. Only then does the JavaScript virtual machine ask, “What’s on the queue?” If there’s at least one event on the queue that’s eligible to “fire” (like a 500ms timeout that was set 1000ms ago), the VM will pick one and call its handler (e.g., the function we passed in to setTimeout). When the handler returns, we go back to the queue. Input events work the same way: when a user clicks a DOM element with a click handler attached, a click event is queued. But the handler won’t be executed until all currently running code has finished (and, potentially, until after other events have had their turn). That’s why web pages that use Java- Script imprudently tend to become unresponsive.

In your specific problem they got fired all together just because you put them into "events array" all together, so their time are pretty same for all of them, and the time you waiting before is just the time you set in setTimeout+minor delta of calculation time.

While programming in Angular, I used this to ensure that my code runs after anything else finished, so I just put setTimeout() without time value, and inside code was executed right after everything else.

Vova Lando
  • 558
  • 3
  • 15
0

Javascript is not multithreaded: the code will continue to execute until it returns to the main loop.

Maurice Perry
  • 32,610
  • 9
  • 70
  • 97
0

The loop occurs 50 times displaying starting

Then in each loop code (function(){})(); is executed as soon as it is read since it is self-invoking!

but the setTimeout will execute after for loop

for-loop timer: timer started
"starting :" 0
selfinvokingfunction timer: timer started 
selfinvokingfunction timer: 0ms
"starting :" 1 
selfinvokingfunction timer: timer started
selfinvokingfunction timer: 0ms 
"starting :" 2 
selfinvokingfunction timer: timer started 
selfinvokingfunction timer: 0ms 
for-loop timer: 656ms 
setTimeout timer: timer started 
"done :" 0 
setTimeout timer: 0ms 
setTimeout timer: timer started 
"done :" 1 
setTimeout timer: 0ms 
setTimeout timer: timer started 
"done :" 2 
setTimeout timer: 0ms

Used console.time and console.timeEnd to check timing the execution sequence is same, whether for loop finishes withing a second or not.