Convincing JavaScript to Multi Task

Harry Turnbull
Makers
Published in
6 min readMay 1, 2020

--

JavaScript isn’t very good at multi tasking, it can only do one thing at a time. But, you can tell it to put something aside for later and do something else in the meantime, with asynchronous functions.

This fridge needs an upgrade! By Ernest Brillo on Unsplash

To illustrate this concept, it is time for another contrived, kitchen related (see previous post involving toasters), analogy.

Imagine you have some chores to do.

  1. There is some grocery shopping to be done.
  2. Curry’s are delivering your new fridge today (other retailers are available).

You create a detailed todo list to plan out your day (in Markdown, because you are of course a programmer):

This looks fine, but what if we are acting like JavaScript and not a normal human being?

The JavaScript Way

Nathália Rosa on Unsplash probably took this before the COVID-19 crisis…

If this was a JavaScript program it might look like this:

First of all we have some variables, objects that represent the shops with some available items, our home (with no fridge), and a delivery with our fridge that is on its way.

The function takeTime simply takes an argument of length, then loops that many million times doing nothing. It’s an easy way of blocking the main thread, causing the execution to wait. It also keeps track of how long that took and logs it to console.

Then there are three functions that take some time (using takeTime) goToShops, goHome and waitForDelivery.

Then finally, there are functions representing items on the todo list: groceryShopping and receiveFridge.

But what order should we do our todo list items?

If we call groceryShopping before receiveFridge we get an error.

Uncaught TypeError: Cannot set property 'contents' of null

This occurs because part of groceryShopping is assigning a value to home.fridge.contents but before the delivery occurs, home.fridge is null which doesn’t have a contents property.

So, we need to do them the other way round, get the fridge first, then go and do shopping.

Waiting for delivery
That took: 4795 ms
Installed fridge
Going to shops
That took: 1152 ms
Finished shopping, going home
That took: 1152 ms
Shopping is in the fridge

But surely as a human capable of multitasking we could go the the shops in the meantime before the delivery comes and wait for the fridge to be installed before trying to put the shopping in the fridge. Or send a housemate to the shops while waiting for the fridge. Or literally any other sensible plan.

Is there a way to get this behaviour from JavaScript?

Getting JavaScript to Multitask

Is that our fridge? Claudio Schwarz on Unsplash

The fix for this is very simple, but the reason why it works is more complex.

Here’s how we can get it working:

Putting the shopping in the fridge has been wrapped with a setTimeout function.

And here’s the results of calling:

Going to shops
That took: 516 ms
Finished shopping, going home
That took: 520 ms
Waiting for delivery
That took: 4740 ms
Installed fridge
Shopping is in the fridge

But Why?

I hear you ask.

Well. setTimeout is a function that takes two arguments. A callback to be executed, and a time to wait in ms.

setTimeout is asynchronous. This means that the callback is put aside until to be executed at a later time so the main thread can continue executing its script.

Try this out in your browser console:

console.log("I go first")
setTimeout(() => console.log("I waited three seconds"), 3000)
console.log("I go second")

How does this work?

I’m glad you asked.

When JavaScript runs in the browser there are several components at work.

The Heap: this is where all variables, functions, objects etc are stored. All the stuff that JavaScript will need to keep track of.

The Stack: this is where execution happens. Things are added to the top of the stack, and removed from the top down to the bottom.

Web APIs: This is where asynchronous functions wait.

Task Queue: This is where async functions get queued for execution when they are ready.

The Event Loop: This controls everything else.

Demonstrating the Event Loop

To keep this as simple as possible, here’s a version of the code without the artificial slowing from the takeTime function:

This will keep what’s going on in the stack simple and easy to follow.

Here’s a diagram representing the state of things just after the script has been started and all the variable and function definitions have been read and added to the heap, but before the functions are actually called.

Next groceryShopping goes on top of that, and each of groceryShopping individual lines go on the stack too.

  1. console.log("Going to shops") is added to the stack. This logs, so can be removed from the stack.
  2. let shoppingBags = [...shops.items] is added to the stack. The variable assignment is executed, so it can then be removed from the stack.
  3. console.log("Going home") is added to the stack. This logs, so can be removed from the stack.
  4. setTimeout is added to the stack. The callback function is sent to the Web APIs to wait for 0 ms. After 0 ms, the callback function is added to the Task Queue.

After everything in groceryShopping is done, it’s popped off the stack as well.

Here’s that process animated:

Next we have the receiveFridge function call, which gets added to the stack, and has its lines added to the stack and executed in turn.

  1. console.log("Waiting for delivery") is added to the stack. This logs, so can be removed from the stack.
  2. home.fridge = delivery.fridge is added to the stack. The variable assignment is executed, so it can then be removed from the stack.
  3. home.fridge.installed = true is added to the stack, variable assigned, and removed from the stack.
  4. console.log("Installed fridge") is added to the stack, is logged, and removed from the stack.

receiveFridge has finished executing, so it also is removed from the stack.

Here’s that process as another animation:

Now, the script has finished running, the script is removed from the stack.

The stack is now empty, so the Event Loop takes the item at the front of the Task Queue and puts it on the stack. This is the callback function from setTimeout:

  1. home.fridge.contents = [...shoppingBag] is added to the stack and the assignment executed, and popped off the stack.
  2. console.log("Installed fridge") is added to the stack, is logged, and removed from the stack.

The callback is now complete, and it is removed from the stack.

Here’s the animation:

seamless animation

After this, JavaScript has nothing to do except chill out and help render the page.

Here’s the full process from start to finish

Big thanks to Tall Tweets for their google slides to gif generator.

All the code is available in this github repo.

Thanks for reading!

--

--

Improviser with Gamez Improv, Improbotics, The Nursery Theatre and Hoopla Impro. Learning to program properly with Makers Academy.