Recently I had some neat discussions at work about synchronous and asynchronous behaviours in JavaScript, so I decided to write down a few lines summarizing how these buzzwords are connected to the characteristics of the language.

Note: I love playing the JavaScript game, but I’m certainly not the JS God Almighty. So please let me know if my knowledge failed at some points in this article.

1 Asynchronous? What’s Asynchronous?

First some theory (we all love computer science theory, right?). General concepts of programming languages introduce two distinct mechanisms of concurrency, namely synchronous and asynchronous control flow.

Searching the Web, we find definitions like the following:

  • synchronous control flow means that a process runs only as a result of some other process being completed or handing off operation
  • asynchronous control flow means that a process operates independently of other processes

So, how does this look in JavaScript? In fact, the above definition does not literally apply here – JavaScript runs in the browser which is itself a single process on the operation system. Even more important than running in a single process, we must understand one of the most important facts regarding JavaScript: it is single-threaded, i.e. it runs in a single thread within the browser process (for further contents on this I recommend an intense Stack Overflow discussion). But what does that mean for us programmers?

2 Synchronous Code

The single-threaded nature of JavaScript has a lot to do with how its statements are executed. Speaking to a child (I love to explain things as if we were children, just read “Sophie’s World”), we could explain synchronous control flow by explaining how to read a book:

  • take the book
  • only when you took the book: read page 1
  • only when you read page 1: read page 2
  • only when you read page 2: read page 3
  • only when you read the last page: burn the book

Let’s translate this into JavaScript:

  • begin the script
  • do crazy stuff
  • only when finished with crazy stuff: do fancy stuff
  • only when finished with fancy stuff: do stupid stuff
  • end the script

Or:

doCrazyStuff();
doFancyStuff();
doStupidStuff();

Each step is executed only when the previous step has completed.

In the following asynchronous examples I’m going to use a rather intense synchronous function for illustration purposes that calculates all prime number up to a given limit. Using 150000 as limit, my machine takes about 3 seconds for this operation:

function calculatePrimes(max) {
  for (var i = 3; i < max; i++) {
    var isPrime = true;
    for (var j = 2; j if (i % j === 0) {
        isPrime = false;
        break;
      }
    }
    if (isPrime) { /*i is a prime number!*/ }
  }
}

3 Asynchronous Code

Taking the above example and the mentioned definition of “asynchronous”, we’d say:

  • take the book
  • no matter if you took the book or not: read page 1
  • no matter if you finished page 1: read page 2
  • no matter if you finished page 2: read page 3
  • no matter if you read the last page, or if you didn’t even take the book: burn the boook

If these actions are asynchronous, we can’t ensure that the first step is finished firstly, the second step secondly etc.

However, since JavaScript is single-threaded, it’s a little different and asynchronous control flow doesn’t exactly mean that “a process operates independently of other processes”. Instead, the script is always run completely first, while all asynchronous actions are queued up by the browser, and only when the script is run completely (the single script thread), the asynchronous actions are executed.

4 Asynchronous Example #1 - Loading an image

Enough time spent writing natural language content. Now let’s look at some code snippets. A simple asynchronous operation is loading an image from the Web:

(function() {
  var img = document.createElement('img');
  img.src = 'http://upload.wikimedia.org/wikipedia/commons/thumb/2/20/Saimiri_sciureus-1_Luc_Viatour.jpg/800px-Saimiri_sciureus-1_Luc_Viatour.jpg';
  img.onload = function(e) {
    document.body.appendChild(img);
  };
}());

Setting the source of the image elements triggers a request to the Wikipedia site to load the image with the given URL. This request is queued up and not executed before the script has finished. (Note that e.g. the Network panel of the Chrome DevTools will already show the request, which doesn’t mean it’s been sent already.) The onload property of the image is assigned an event handler that’s executed as soon as the image has fully loaded. Ah, wait! Yes, the event handler is executed asynchronously. More or less. Or, not according to the definition above, because as mentioned earlier, it will only be executed when the script thread in which it’s being defined has completed. So, it’s in fact not independent from the script itself.

The quintessence of this is that we can place arbitrarily complex operations between setting the src attribute and setting the onload handler. Let’s use the above prime number function:

(function() {
  var img = document.createElement('img');
  img.src = 'http://upload.wikimedia.org/wikipedia/commons/thumb/2/20/Saimiri_sciureus-1_Luc_Viatour.jpg/800px-Saimiri_sciureus-1_Luc_Viatour.jpg';
  img.onload = function(e) { //
    document.body.appendChild(img);
  };
  calculatePrimes(150000); // an intense synchronous operation!
}());

Calculating the prime numbers takes about 3 seconds on my computer, whereas loading the image only takes less than 1 second. Now note: the function assigned to the image’s onload property will be called asynchronously, whereas the calculatePrimes function
will be called synchronously, i.e. it is called within the same script thread. This means that we can be 100% sure that the calculatePrimes function is called before the onload function, because the script thread is fully executed first. Consequently, the calculatePrimes function may take one year and the onload function may take one milisecond, the calculatePrimes function will always execute first!

Another interesting fact: this also implies that it doesn’t matter at which point we associate the onload handler, as long as it’s in the same script. If we did it like this…

(function() {
  var img = document.createElement('img');
  img.src = 'http://upload.wikimedia.org/wikipedia/commons/thumb/2/20/Saimiri_sciureus-1_Luc_Viatour.jpg/800px-Saimiri_sciureus-1_Luc_Viatour.jpg';
  calculatePrimes(150000); // an intense synchronous operation!
  img.onload = function(e) { //
    document.body.appendChild(img);
  };
}());

…the unexercised reader could mistake this as a classical race condition – the image is loaded much faster than the calculatePrimes function. Then the onload handler will not yet be bound to the image when it has already loaded completely, so it will never be called and the image won’t be appended to the page. Wrong! Due to the single-threaded nature of JavaScript, the sequence is as follows:

  • save the request to load the image in the queue of asynchronous operations
  • calculate the primes
  • bind the onload handler on the image object
  • when the script is finished, send the request to load the image
  • when the image has fully loaded, call the onload handler

Always remember: the whole script is executed before any asynchronous action is performed!

5 Asynchronous Example #2 - setTimeout

Another example giving some headaches to developers is JavaScript’s setTimeout function. (The same applies to setInterval which we know to be eevil, of course.)

(function() {
  document.body.style['background-color'] = '#bada55';
  setTimeout(function() {
    document.body.style['background-color'] = 'PeachPuff';
    // (ever heard of this crazy color? - it's not particularly beautiful in fact)
  }, 1);
}());

We are setting the site’s background color to a badass light green before we set it to PeachPuff one millisecond later, using the setTimeout function, which is called asynchronously. So even if the timeout is set to only one millisecond, guess what happens if we do the following:

(function() {
  document.body.style['background-color'] = '#bada55';
  setTimeout(function() {
    document.body.style['background-color'] = 'PeachPuff';
  }, 1);
  calculatePrimes(150000);
}());

Same again: the function given as callback to setTimeout must wait for the calculatePrimes function, although its compution time is 3000x higher.

We can take things to the limit:

(function() {
  setTimeout(function timeout1() {
    setTimeout(function timeout2() {
      setTimeout(function timeout3() {
        // and so on... I think you get the point :)
      }, 1);
      // everyting here will be executed before timeout3
    }, 1);
    // everyting here will be executed before timeout2
  }, 1);
  // everyting here will be executed before timeout1
}());

It is actually quite important to understand that an operation is asynchronous
as soon as a setTimeout or setInterval statement is included: libraries like jQuery
use setTimeout heavily for animations. So guess why your animation isn’t running
as you expect?

6 Asynchronous Example #3 - jQuery and Ajax

No one can imagine a world without jQuery anymore. It’s our daily business to do things like:

(function() {
  $.ajax({
    url: 'https://www.scandio.de/',
    data: { 'param': 666 },
    success: function() {
      // do something on success
    }
  });
}());

However, suppose you do a lot of synchronous stuff after the Ajax call:

(function() {
  $.ajax({
    url: 'https://www.scandio.de/',
    data: { 'param': 666 },
    success: function() {
      // do something on success
    }
  });
  // Seven...
  // ...statements...
  // ...to...
  // ...rule...
  // ...them...
  // ...all...
  // !
  // And somewhere down here where you already stopped thinking
  // about your Ajax call:
  while (['...', 'good', 'beers', '...'].indexOf('becks') < 0) {
    // This will be called over and over again because 'becks' will
    // never be in the array of good beers! This is an infinite
    // loop!!
  }
}());

The script gets stuck in an infinite loop and the Ajax request will never fire, because the script thread won’t finish. That feels weird in some situations where the Ajax call is completely somewhere else in the code than the synchronous operation that takes a long time.

7 Asynchronous Example #4 - IndexedDB

One more. Maybe you’ve heard about the crazy concept of IndexedDB, the NoSQL database running in modern browsers. The version currently supported handles all database interactions as asynchronous transactions.

I learned a lot about JavaScript’s asynchronous handling reading over the comments very interesting comments (!) of Mozilla’s introduction of IndexedDB:

To open a database with IndexedDB, we do the following:

(function() {
  var request = window.indexedDB.open('ScandioDB', 'My Scandio Database');
  request.onsuccess = function(event) {
    // what if I'm not yet assigned when the database was opened?
  };
}();

At first glance, this clearly looks like a race condition thing: I open the database and assign a success handler afterwards. How do I make sure the database won’t open faster than the point where the success handler is assigned? Same answer as earlier:

  • JavaScript is single-threaded
  • asynchronous operations (like indexedDB.open above) are queued up and
    executed after the whole script was executed

The open procedure will always execute after the onsuccess function was assigned, because it is asynchronous.

8 Don’t be mislead

Throughout all the examples in this article, I gave functions as argument to other functions. This is possible because in JavaScript, functions are objects. However, the fact that a function is given as an argument has nothing to do with whether it’s synchronous or asynchronous. We could implement asynchronous traffic without giving functions as arguments.

The following snippet might explain better:

(function() {
  var crazyFunc = function(callbackFunc) {
    callbackFunc();
  };
  crazyFunc(function() {
    console.log('hello');
  });
  // some
  // loong
  // pieces
  // of
  // code
  console.log('scandio');
}());

The above example does not include any asynchronous operations. Therefore the callback function that logs ‘hello’ will be called before the ‘scandio’ log. Just to make sure you don’t mistake function arguments as always aynchronous.

9 Conclusion

In this article, I tried to show how the JavaScript language plays with synchronous vs. asynchronous control flow, because I discovered that especially the call sequence can be rather misleading in some cases. Several typical asynchronous examples illustrated that the pattern is always similar and fairly simple if once understood. Hope you’ll wonder fewer times why your Ajax or animation handler doesn’t run at where you’d expect.