Joseph Joseph - 4 months ago 18
Javascript Question

javascript SetTimeout only invoked at function exit

I've noticed that

setTimeout(function, milliseconds)

when used in middle of function will only be executed once function has ended regardless of timming given to it,

for example:



function doStuff(){
var begin = (new Date()).getTime();
console.log(begin);
setTimeout(function(){console.log("Timeout");},100);
doWork(4000);
var end = (new Date()).getTime();
console.log(end);
console.log("diff: "+(end-begin));

}


function doWork(num){
for(;num>0;--num) console.log("num");
}

doStuff();





the code above sets the timeout for 100 milliseconds but it is invoked only after all function completes which is much more then 100 milliseconds,

my question is:


  1. why does this happen ?

  2. how can i insure correct timing ?


Answer

JavaScript is not pre-emptive: it first finishes what it is doing before looking what is the next task that was posted in the queue (functions submitted for asynchronous execution). So even if a time-out expires, this will not influence the currently running code -- it does not get interrupted. Only when the stack of currently running functions have all returned and nothing remains to be run, JavaScript will check if there is a time-out request to be fulfilled, or whatever else is first in the queue.

From "Concurrency model and Event Loop" on MDN:

Queue

A JavaScript runtime contains a message queue, which is a list of messages to be processed. A function is associated with each message. When the stack is empty, a message is taken out of the queue and processed. The processing consists of calling the associated function (and thus creating an initial stack frame). The message processing ends when the stack becomes empty again.

Event loop

The event loop got its name because of how it's usually implemented, which usually resembles:

while(queue.waitForMessage()){
  queue.processNextMessage();
}

queue.waitForMessage waits synchronously for a message to arrive if there is none currently.

"Run-to-completion"

Each message is processed completely before any other message is processed. This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it can be stopped at any point to run some other code in another thread.

A downside of this model is that if a message takes too long to complete, the web application is unable to process user interactions like click or scroll. The browser mitigates this with the "a script is taking too long to run" dialog. A good practice to follow is to make message processing short and if possible cut down one message into several messages.

To get code to run concurrently, you can make use of Web Workers. Web Workers run scripts in different threads.

Comments