Reasoning about Asynchronous JavaScript

Kevin O'Shaughnessy
8 min readJul 7, 2016

--

Welcome to this review of the new Pluralsight course Reasoning about Asynchronous JavaScript by Wes Higbee.

Wes Higbee is passionate about helping companies achieve remarkable results with technology and software.

He’s had extensive experience developing software and working with teams to improve how software is developed to meet business objectives.

He writes extensively about both technical and non-technical aspects of software development at weshigbee.com

Reasoning About Asynchronous JavaScript

Wes skips the introductions and jumps straight into the questions:

  • How will this plunk behave?
  • Do you have a lot of experience working with other languages that are multithreaded?
  • Have you wondered how and when JavaScript executes your code?
  • Have you ever tried to do things in parallel in JavaScript?
  • Have you ever had computationally intensive JavaScript code locking up the browser?
  • Have you ever thought about the complexity of the code that you’re writing and whether it might take up too many resources in the browser?
  • Have you ever wondered why race conditions don’t seem to be such a big deal in JavaScript?
  • Have you heard that JavaScript is single threaded? Is that a weakness?
  • Do you find callbacks confusing?
  • Have you heard of event loops, run to completion, cooperative concurrency, and non blocking I/O?

If some of these questions are of interest to you, this could be the course for you.

Most languages have preemptive concurrency. JavaScript is single threaded and has a different concurrency model: cooperative concurrency.

Wes explains the objective of the course is to help us reason explicitly about when our code will run.

This course has been categorized by Pluralsight as Advanced, but Wes says only a basic understanding of JavaScript and a desire to learn is required.

@ZombieCodeKill Also thanks for pointing out that it is flagged as Advanced, it should be Intermediate.

— Wesley Higbee (@g0t4) July 7, 2016

How to use this course

Wes stresses the importance of practicing and applying the lessons taught in this course. There are several challenges for you throughout the course to try out.

Wes has at least one more course on asynchronous JavaScript coming out soon. You can think of this course as an introduction to asynchronous programming in JavaScript. We will not be writing a lot of code, just reasoning about how, why and when existing code works.

Links to Examples

Many of the examples use Open Weather Map so it’s advisable to sign up for an account there if you don’t already have one.

Single Threaded

Wes shows us the MDN article Concurrency Model and Event Loop, highlighting the second sentence:

This model is quite different than the model in other languages like C or Java.

Experience working with other languages can give us a “multithreaded mentality”.

All JavaScript engines are single threaded, so only one thing can be executed at any one time.

Event Loop

Wes uses a line of people waiting to use a bathroom as an analogy for the queue used in a JavaScript event loop.

Non Blocking

The “multithreaded mentality”that tends to develop from working with other languages can hinder our ability to write performant JavaScript code.

We look at the weather plunk. They is quite a lot of code here, but the heart of it, and the most important part to focus on, is the load function.

This plunk runs quickly despite all being on on the same thread. This is due to the use of callbacks which allow us to continue running the rest of our code before we get the results of other code.

For example we make a weather request before our 5 day forecast request, but we don’t need to wait for the results of our weather request before we can make our 5 day frecast request.

How is this possible? We can get some clues by opening the Network tab in our browser’s Network tools.

Wes filters the timeline down to only show the web requests. They’re executed at the same time.

Despite being single threaded, we can still achieve parallelism in JavaScript.

How to Avoid Blocking

Although JavaScript supports non blocking constructs, we could still accidentally write something that’s blocking. Line 23 of the weather plunk:

weatherRequest.open(‘get’, weatherUrl, true);

Look at the XMLHttpRequest.open documentation on MDN. The 3rd argument specifies whether it is asynchronous.

Wes demonstrates the effect of the flag by adding console.log statements and showing the order of execution with this flag on and off.

Run to Completion

This uses the avoid blocking plunk, the same example Wes begun the course with.

The key takeway (other than not to hog the bathroom) is the event listener runs to completion. This makes it especially important to not write long running code here.

Wes also returns to the weather plunk to explain the code execution in more detail.

Challenge — Run to completion

After discussing the weather plunk some more, we look at the setTimeout pyramid challenge.

Cooperative Concurrency

Wes highlights this article and in particular the image of everyone waiting in line and unhappy outside the DMV.

In JavaScript it is more like waiting for a single bathroom where the person using the resource has control over how long to be there for.

Okay enough of the analogies! How is it possible for JavaScript to be concurrent, when it is single threaded?

It is like humans — we can’t really multitask, we just switch context very, very quickly. And in JavaScript it seems like things are running at the same time as long as those things don’t take a long time to process.

Again see this MDN article for further details.

Animated Walkthrough of When Code Executes

Back of the Weather plunk again, or rather a subset of it.

Wes talks through the hypothetical execution of this program, covering:

  • the Event Loop and its Queue
  • the Call Stack and the code executing on it
  • the Button Clicked event, load method, console.log, send weather etc.

Wes says the browser can have multiple threads executing multiple requests at the same time.

Our JavaScript engine is single threaded, but other things can happen outside of our JavaScript engine.

Mentality: Little Programs

In order to reason about Asynchronous JavaScript, Wes likes to think of a JavaScript program as having a number of little programs nested inside it.

In this lesson he illustrates each function as a little programs, and these are the asynchronous seams inside of our application.

Exercises and Examples: Sources of Async

We learn that the sources of asynchronicity include:

  • User Interactions
  • Network I/O
  • Disk I/O — reading and writing files
  • Inter Process Communication e.g. communicating back and forth with Web Workers
  • Timers

The following lessons demonstrate each of these.

User Interactions

Exploring the Drag and Drop — Red Box plunk.

Drag and Drop is an HTML5 feature that is not available in all browsers.

This plunk has console log messages so we can see which functions are getting executed and when.

How many “little programs” are involved in this example? Have a think about it, and then watch this clip for the answer.

Timers

Wes shows us the MDN setTimeout and the Node Timers API documentation.

With Node JS there are new ways to schedule timers.

Next we look at the timers onReady plunk and the problem of trying to run JavaScript before all of the relevant DOM elements are ready. Wes describes this as a race condition.

There’s no guarantee of when functions inside of setTimer are executed — it all depends on the other work that is already sitting on the queue at the point where our function is added onto the end of it.

Timer Delay Gotchas

Our timer measure plunk measures the difference in time between the requested delay and the actual average delay. We can change the number of repetitions, and the requested delay in milliseconds, and observe it’s effect.

The shortest possible delay is 4 milliseconds, so anything less than that will actually mean 4 milliseconds.

This example uses ES6 syntax, so it won’t work in older browsers.

How many little programs in the timer measure plunk? The answer may bend your mind a little bit.

Not Always Async

A couple of trick questions from Wes as he asks us how many little programs, and how will the program behave? There is no way that we can tell!

Then we see the complete code listing. Does it make sense to you now? Things aren’t quite what they appear to be.

Wes updates the code so that it behaves asynchronously.

The key point is to remember to verify your assumptions when reading code.

Debugging Assumptions

Wes returns to the weather plunk once more and shows us how to use debugging as a tool for figuring out where all the asynchronous seams, (“little programs”) exist.

This makes use of the browser’s Call Stack and many break points.

Debugging with Async Call Stacks

Chrome Dev Tools has a relatively little known checkbox labelled Async.

Wes shows us how this works, and we see additional information in the Call Stack — where our asynchronous calls have come from.

Disk

This example uses Node JS. How many little programs are there?

Callbacks are a notorious sign that a function is asynchronous in Node JS, but we cannot just assume that. We should check the documentation where it exists, or reason about the code if it does not.

Node JS nextTick and setImmediate

There is good documentation available for these API functions:

We see a demonstrations of these functions and their effects.

Web Workers

Wes shows an example with a blocking resource, giving an awful user experience: we see the UI appear after more than 10 seconds.

It’s because, as we learned earlier, the blocking function needs to run to completion before the rest of the code can execute.

We can get a better understanding of how this code works by adding the console.log statements, and we see that at least in Chrome, logging to the console is not blocked.

Now with a few tweaks to this code, splitting it out into background.js and crunchNumbers.js, we get the progress bar working entirely as we want it to.

We see that using Web Workers is much more straightforward than writing asynchronous code in some other languages.

The single threaded nature of JavaScript reduces the complexity for us, and race conditions are rare, but not entirely non existent…

Race Conditions

Back to the weather plunk again.

We see Race conditions can still happen because the order of items in the queue is not guaranteed.

Event Listeners are Synchronous

In this final example, we see two event listeners applied to the same button, and the results may be different from what we expected.

Wes steps through it in the debugger explaining how the code works.

What’s Next

Wes reiterates all the main points we learned in this course.

He has followed this up with course on JavaScript promises: Modern Asynchronous JavaScript.

--

--

Kevin O'Shaughnessy

Sr. Full Stack Web Dev promoting better professional practices, tools, solutions & helping others.