Asynchronous Javascript for Beginners (pt. 3)

In this post we’ll discuss how asynchronous code has been dealt with up until the last few years. Namely, callbacks. Let’s start with the most basic example we have, a setTimeout call:

setTimeout(function(){ console.log(“Hello”); }, 3000);

As we’ve discussed, setTimeout is asynchronous. And so here it takes a callback function. What setTimeout does is calculate where in the process que it can slide that callback in that will occur AT LEAST three seconds later.

Now we have to take a quick break here because something is rearing its head already which, if we don’t take care of it, will lead to confusion. You might have heard that javascript is single-threaded. That only one process can be happening at any one time. Well if that’s the case, how can we have async at all? Isn’t that what asyc is, things happening at the same time? The answer is…well….. almost. But not quite! Your computer that you’re running right now is capable of truly running multiple processes at the same time, what we refer to as things running in parallel (as opposed to async). That’s how your machines can handle dozens of apps being open at the same time, all while handling tons of other processes behind the scenes that you never even see. Your computer is capable of running thousands of threads all at once! Pretty amazing, huh?

Well, don’t get too excited, because we don’t get to have those threads in our JS app. We get one. Javascript engines are single-threaded, meaning that we can only do one thing. At a time. So with this information on board, what is async in this new context?

It’s a trick. It’s a way of making your application SEEM likes it’s multi-threaded, even though it’s not. The reason we do this is for user experience.  If users see that SOMETHING is happening, it’s a lot easier to wait than if their screen just freezes for ten seconds, the result of allowing blocking processes in your app. So let’s get back to the main topic: callbacks and the problems they created. Let’s look at a simple AJAX request made with jQuery:

$.get( "ajax/test.html", function( data ) {
alert( "Load was performed." );
});
Pretty simple. Readable, easy to reason about. The problem is, what you wanted to do with a request usually wasn’t this easy. And when things got more complicated, you ended up with callback after callback, in a tangled maze that has come to be known as callback hell. Here is an example, taken from callbackhell.com :

fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})
The characteristic shape of this type of code came to be known as the pyramid of doom. However, even if the callbacks were extracted out into separate, external functions, we would find that it is still hard to reason about and that callback hell goes deeper than nesting and indentation. In the next post, we will explore those problems, and the solutions that were devised.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s