Question

I've been reading John Resig's "Secrets of a JavaScript Ninja" and it explains that JavaScript is single-threaded. However, I tried testing this and I'm not sure what to take away from here:

// executing this in browser
(function () {
    // throw something into event queue
    setTimeout(function () {
        alert("This will be called back after 1 second.");
    }, 1000);

    // arbitrary loop to take up some time
    for (var i = 0; i < 10000; i += 1) {
        console.log(i);
    }
})();

Maybe I'm not understanding exactly what being single-threaded means, but I thought that the setTimeout callback wouldn't execute until all of the outer anonymous function is complete. However, running this in the browser shows that the callback function gets called while i's are still being outputted onto the console. To me, this seems like there's 2 threads with anonymous function's invokation occupying 1 thread and then the callback using the 2nd thread.

Can someone help un-confuse me?

Was it helpful?

Solution

console.log() is a weird function in some browsers (like Chrome) and is not synchronous itself so you can't really use it to gauge the single threadedness. What you are probably seeing is that the JS engine executes all the console.log() statements and then the setTimeout() runs to show the alert and, in parallel (in some other process that isn't javascript) all the data is being shown in the console.

Javascript is indeed single threaded. In your example, the setTimeout() callback will not execute until your for loop is done.

You can illustrate it better like this:

(function () {
    // throw something into event queue
    setTimeout(function () {
        alert("This will be called back after 1 second.");
    }, 1000);

    function now() {
        return new Date().getTime();
    }
    var start = now();
    // loop for 1.2 seconds
    while (now() - start < 1200) {}
    alert("Looping done");
})();

Working jsFiddle demo: http://jsfiddle.net/jfriend00/3sBTb/

OTHER TIPS

This is a bit of a tricky concept to understand. Throwing in things like event listeners also further muddies up the picture.

A simple way to think of it is as if you have a conveyor belt. You have your normal function calls all evenly spaced out, with room in between.

Things that are asynchronous things (timeouts, triggered events, etc.) fill those spots. There isn't an infinite amount of room between each of those normal calls, so it fits what it can from this queue, does a little more of the normal synchronized functions, fills some more space with asynchronous, etc.

The affect appears to be somewhat multi-threaded (and indeed you can cause race conditions of a sort with asynchronous calls). In many cases, the distinction doesn't matter. However, it is important to remember this.

However, when you try to debug things, especially when using tools like Chrome's console.log, it can look like things are scrambled because console.log itself is asynchronous (if it were synchronous, it would freeze your script on a long function).

You can see this yourself if you output something like this:

var input = document.createElement('input');
input.setAttribute('value', 0);

function inc() {
    input.setAttribute('value', parseInt(input.getAttribute('value'))+1);
    console.log(input);

    if (parseInt(input.getAttribute('value')) < 100) {
        setTimeout(inc, 10);
    }
}

inc();

JSFiddle: http://jsfiddle.net/c2PnP/

What this script does it creates an input element, then every 10 milliseconds, it increments the value of input, then outputs the input element. It repeats 100 times (until value = 100).

If you look at your console, you'll notice that some of the values will be duplicated, it won't be a smooth progression. For example, on a run I just did, I see 5 inputs with a value of "100" and gaps for the missing numbers. This is because console.log is running asynchronously and only outputting when it gets the gap to do so.

(Note: If you have a super fast computer, you may need to decrease the time to something smaller, like 1, and/or increase the number of iterations to a bigger number).

John Resig covered this well. Summarizing:

"JavaScript can only ever execute one piece of code at a time (due to its single-threaded nature)... when an asynchronous event occurs (like a mouse click, a timer firing, or an XMLHttpRequest completing) it gets queued up to be executed later.... After the initial block of JavaScript finishes executing the browser immediately asks the question: What is waiting to be executed? The browser then picks one and executes it immediately. The [rest] will wait until the next possible time, in order to execute."

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top