JavaScript single-threadedness and timers

If you’re coming from a programming language that has support for multithreading and concurrency, (such as Java) then understanding the flow of asynchronous events in JavaScript such as timers can be a bit confusing.

However, once you understand the single-threaded nature of most JavaScript, things may actually becoming easier as you don’t have to worry about parallel access to non-local variables.

Let’s take a look at how JavaScript executes when a function is passed to window.setTimeout().

Multithreading

As anyone who’s done a fair amount of Java will know, it’s fairly easy to easy to spawn a new Thread, usually by submitting a new Runnable to a Thread Pool or ExecutorService, or less frequently, by directly creating an instance of Thread and calling start(). Since this post isn’t about Java, I won’t talk anymore about it, other than to say when you do this, you have created/spawned another thread – so you now have at least two threads: The one you just created and the original/main one that created the second thread.

The first thing you’ll realize is that with multiple threads executing, by default there is no defined order to how they will execute. So, if I spawned two threads, one printing "a" to stdout repeatedly and one printing "b" to stdout, there is no guarantee of the order the letters would appear on screen and furthermore, it would likely vary from execution to execution of the program.

This leads to the need for synchronization and locks on concurrent code in order to maintain a certain level of ordering in how concurrent code operates. Is the same true for JavaScript?

JavaScript Timers

I will focus my example on the setTimeout() timer, though the same principle I’l be demonstrating holds for other asynchronous events.

What will be the value of output after the following code has executed?

var output = '';
var iterations = 10000000;
setTimeout(function() {
  for (var i = 0; i < iterations; ++i) {
    output += 'a';
  }
}, 100);
setTimeout(function() {
  for (var i = 0; i < iterations; ++i) {
    output += 'b';
  }
}, 100);

In this example, it would seem that we have kicked off the execution of two long-running functions at approximately the same time and both are modifying/mutating a global/shared variable.

If this example involved multithreaded execution, the value of output would have several alternations between the letters ‘a’ and ‘b’.

Conversely, if output consisted of a solid block of ‘a’ followed by a solid block of ‘b’, we could conclude that it was likely the first function executed completely before the second function was allowed to execute.

In my brief tests in Chrome 33.0 and Firefox 27.0 this was the case: A solid block of ‘a’, followed by a solid block of ‘b’. (You can see the full example/test in this gist.)

This would seem to indicate that there is only a single thread of execution in JavaScript, as the execution of one function completely blocked the execution of another.

A little deeper

What’s really going on is that the calls to setTimeout are asynchronous. This means they return (almost) immediately and instead just queue up an event to invoke the passed-in function later. This means that the function that writes ‘a’ to output will execute before the function that outputs ‘b’.

How the queue works is likely implementation-specific, but when an event is read off the queue and processed, it blocks while that event/function is being executed.

This behaviour doesn’t just apply for JavaScript timers like setTimeout/setInterval but for all asynchronous event handling in JavaScript.

This is probably a key point: Asynchronous does not mean parallel! In the case of JavaScript, the concurrency is actually accomplished through this sort of context-switching, rather than having separate threads that actually execute/run in in parallel/at the same time.

Web Workers

One important caveat: The above discussion did not involve Web Workers, which is an API that does allow you to explicitly create separate threads of processing in JavaScript. However, you must explicitly spawn these new threads and they can only communicate with the main thread through the use of specific APIs/event handlers.

By default, variables in the main thread aren’t shared with the worker threads. I suspect this was done to avoid problematic concurrent data-access issues.

However, the Web Workers API does not affect how events such as setTimeout work – everything above still holds.

References

  1. How JavaScript Timers Work
  2. Minimum/maximum delay and timeout nesting
  3. Introduction to HTML5 Web Workers: The JavaScript Multi-threading Approach
  4. JavaScript’s Strange Threaded Nature

Comments for this entry are closed

But feel free to indulge in some introspective thought.