Asynchronous Javascript: SetTimeout and SetInterval

SetTimeout and SetInterval provide ways to schedule tasks to run at a future point in time.

setTimeout allows you to schedule a task after a given interval.

setInterval lets you run a task periodically with a given interval between runs.

SetTimeout

setTimeout takes two parameters:

A string representing code to run and a number representing the time interval in milliseconds to wait before executing the code.

In the following example, the browser will wait two seconds before executing the anonymous function and presenting the alert message.

let myGreeting = setTimeout(function() {
  alert('Hello, Mr. Universe!');
}, 2000)

We’re not required to write anonymous functions. The second version of the example uses sayHi as the name of the function. The rest of the code remains unchanged.

let myGreeting = setTimeout(function sayHi() {
  alert('Hello, Mr. Universe!');
}, 2000)

The code is rather messy. We can clean up the setTimeout call by taking the function outside the setTimeout call. The next iteration of our code defines sayHi first and then references the function by calling sayHi without parenthesis as the first parameter of setTimeout.

function sayHi() {
  alert('Hello Mr. Universe!');
}

let myGreeting = setTimeout(sayHi, 2000);

The last step in the demo is to pass parameters to the function we want to use in setTimeout.

This gets a little tricky.

First, we configure the function to add the parameter and use it in the body of the function.

When we call setTimeout we pass the values for the function parameters as the third (and subsequent if there is more than one) parameters.

function sayHi(who) {
  alert('Hello ' + who + '!');
}

let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');

All versions of the function will produce the same result… but they show different ways we can use setTimeout and the flexibility we have in writing the code.

ClearTimeout

This is less of an issue with setTimeout as it is with setInterval (discussed in later sections) but there may still be situations where you want to abort the execution of code inside a setTimeout call. For example, let’s say that we set the timeout for a very expensive task.

function runExpensiveTask() {
  alert('Expensive Task has been completed!');
}

let myTaskRunner = setTimeout(runExpensiveTask, 30000);

And we want to stop it because we want to do something else on the page. To do it we call clearTimeout with the id of we assigned to setTimeout when we created it.

let forgetIt = clearTimeout(myTaskRunner);

clearTimeout() and its cousin clearInterval() use the same list of entries to clear from. This means that you can use either method to remove a setTimeout or setInterval. For consistency, I use clearTimeout to clear setTimeout() entries and clearInterval to clear setInterval() entries.

setInterval

setTimeout works perfectly when we need to run the code once after a set period of time. But what happens when we need to run the code every x milliseconds?

That’s where setInterval comes in. When we use this command, the code we attach to it will run every time the interval completes.

The example below creates a new date object and logs it to console. We then attach it to setInterval and execute it once every second. This will create the effect of a running clock that updates once per second.

function countTime() {
    let date = new Date();
    let time = date.toLocaleTimeString();
    document.getElementById('demo').innerHTML = time;
}

const createClock = setInterval(countTime, 1000);

clearInterval

With repetitive tasks like our clock example, we definitely want a way to stop the activity… otherwise, we may end up getting errors when the browser can’t complete any further versions of the task.

stopTime() clears the interval with the ID we created.

The example goes further and creates a button and attaches the stopTime function to the button’s click event so when we click the button the interval will stop.

function stopTime() {
    clearInterval(createClock);
}

let myButton = document.getElementById('stopButton');
myButton.addEventListener('click', stopTime);

Things to keep in mind

There are a few things to keep in mind when working with setTimeout and setInterval.

Recursive Timeout

There is another way we can use setTimeout: Use it recursively to call the same code repeatedly instead of using setInterval.

Compare the first example using a recursive setTimeout to run the code ever 1000 milliseconds.

let i = 1;

setTimeout(function run() {
  console.log(i);
  setTimeout(run, 100);
}, 100);

The second example uses setInterval to accomplish the same effect of running the code every 100 milliseconds.

let i = 1;

setInterval(function run() {
  console.log(i);
}, 100);

The difference between the two versions of the code is a subtle one.

Recursive setTimeout guarantees a delay between the executions; the code will run and then wait 100 milliseconds before it runs again. The 100 milliseconds will happen regardless of how long the code takes to run.

Using recursive setTimeout guarantees the interval will be the same between executions. Image taken from https://javascript.info/settimeout-setinterval.

The example using setInterval does things differently. The interval we choose for setInterval includes the code we want to run in its execution. Let’s say that the code takes 40 milliseconds to run, then the interval ends up being only 60 milliseconds.

The interval we set for setInterval includes our own code execution. Image taken from https://javascript.info/settimeout-setinterval.

Immediate Timeout

Using 0 as the value for setTimeout schedules the execution of func as soon as possible but only after the current code is complete.

For instance, the code below outputs “Hello”, then immediately “World”:

setTimeout(function() {
  alert('Mr. Universe')
}, 0);

alert('hello');

When would you use them?

setTimeout and setInterval are useful when you need to schedule code execution.

Use setTimeout if you want to execute the code once after a given time elapses. Pay attention to the gotchas for using setTimeout and consider them additional alternatives

Use setInterval if you need the code to execute repeatedly at given intervals.

Asynchronous Javascript: Introduction and Synchronous Code

When we write Javascript we normally do it synchronously, every instruction is executed one at a time in the order they appear in the script. The script will finish once the last instruction is executed.

In the example below, we log three items to the console.

console.log('1');
console.log('2');
console.log('3');

We will always get the same result: 1 2 3.

This works great for small pieces of code or scripts that don’t produce output on the browser. But sooner rather than later you will want to work on larger scripts and you will find one of the first issues with Javascript: It blocks rendering.

Let’s take the following HTML document.

<!DOCTYPE html>
<html lang='en'>
  <head>
    <meta charset='UTF-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <title>Document</title>
    <script src='myscript.js'></script>
  </head>
  <body>

  </body>
</html>

And the following content in myscript.js:

// Capture the body tag as the third child of the document (zero based)
const content = document.documentElement.childNodes[2];
// Create an h2 element and assign it to the header constiable
const header = document.createElement('h2');
// Add content to the header
header.innerHTML = 'This was inserted';
// Assign it as the first child of the body element
content.insertAdjacentElement('afterbegin', header);

The browser will load the page and when it finds the script it will load it and process it. Because the browser can not know ahead of time if the script will insert, remove or change content on the page it will pause rendering until the script is fully parsed and any changes to the page like inserting or removing nodes are done. The script can have any number of functions and interact with databases and other resources on your page and application.

This will happen for every script that you add to the page; they will each load completely before moving onto the next. Working with smaller scripts may be fine but imagine if you’re loading jQuery from a Content Delivery Network and then loading our script. Our HTML will look like this:

<!DOCTYPE html>
<html lang='en'>
  <head>
    <meta charset='UTF-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <title>Document</title>
    <script
      src='https://code.jquery.com/jquery-3.3.1.min.js'></script>
    <script src='myscript.js'></script>
  </head>
  <body>
    <!-- content goes here -->
  </body>
</html>

Even with no content, this page will take longer to load as it now has to download jQuery before it can continue processing the page and its content, which may include other scripts.

Things will get slower when the code we write does heavy processing like database access or image manipulation.

The figure below gives you an example of what the execution order or stack looks like for a synchronous blocking language like Javascript.

Before we jump into asynchronous (async) code we’ll look at two ways of writing synchronous code: Callbacks and try/catch blocks.

Callbacks

Callbacks are functions that are passed as parameters to other functions.

An example of a callback is the second parameter of an event listener. In the example below, clicking the button with id of myButton will trigger the function and produce the alert for the user.

myButton.addEventListener('click', function() {
  alert('You Clicked THE Button');
})

Another example is when we use forEach to loop through the items in an Array. In this case forEach will loop through the array

const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];

gods.forEach(function (eachName, index){
  console.log(index + 1 + '. ' + eachName);
});

The two examples work because Javascript allows functions as parameters in other functions.

When we pass a callback function as an argument to another function, we are only passing the function definition. We are not executing the function in the parameter. The containing function can execute the callback anytime.

Note that the callback function is not executed immediately. It is “called back” (hence the name) somewhere inside the containing function’s body synchronously.

Working with ‘this’

When the callback function uses the this object, we have to modify how we execute the callback function to preserve the this object context. Or else the this object will either point to the global window object (in the browser) if callback was passed to a global function. Or it will point to the object of the containing method.

const agentData = {
  id: 007,
  fullName: 'Not Set',
  // setUserName is a method on the agentData object
  setUserName: function (firstName, lastName)  {
    this.fullName = firstName + ' ' + lastName;
  }
}

function getUserInput(firstName, lastName, callback)  {
  if ((firstName.length > 3) && (lastName.length > 3)) {
    callback (firstName, lastName);
  } else {
    console.log('data could not be saved.');
  }
}

When we create a new agent something unexpected happens. agentData.fullName returns the default value of Not Set but when we check for fullName in the window object (window.fullName) we get the name we expected.

getUserInput ('James', 'Bond', agentData.setUserName);
console.log (agentData.fullName);
console.log (window.fullName);

The problem has to do with this.

In most cases, the value of this is determined by how a function is called. It can’t be set by assignment during execution, and it may be different each time the function is called.

When we call a function directly, like we call getUserInput, the context is the root object (window in the case of the browser)

When a function is called as a method of an object, its this is set to the object the method is called on.

So how do we solve this problem?

Call and Apply

We can fix the problem with this using call or apply. These methods (available to all functions) are used to set the this object inside the function and to pass arguments to the functions.

Call takes the value to be used as the this object inside the function as the first parameter, and the remaining arguments to be passed to the function are passed as a comma-separated list.

For call and apply to work we add an extra parameter that designates what object we want to use to represent this, in this case, we call it callbackObj.

The biggest difference is that our new version of getUserInput uses the call method of the callback function with four parameters, including the new callbackObj.

const agentData = {
  id: 007,
  fullName: 'Not Set',
  // setUserName is a method on the agentData object
  setUserName: function (firstName, lastName)  {
    this.fullName = firstName + ' ' + lastName;
  }
}

function getUserInput(firstName, lastName, callback, callbackObj)  {
   if ((firstName.length > 3) && (lastName.length > 3)) {
    callback.call (callbackObj, firstName, lastName);
  } else {
    console.log('data could not be saved.');
  }
}

To verify that it works we add a new agent with our getUserInput function and, here is the key, we tell it that we want to use agentData as the object to represent this.

When we log the value of agentData.fullName we get the expected value. When we try to log the value of window.fullName we get undefined because this means agentData not the global window object.

getUserInput ('James', 'Bond', agentData.setUserName, agentData);

console.log (agentData.fullName); // James Bond
console.log (window.fullName); // undefined

The Apply function’s first parameter is also the value to be used as the this object inside the function, while the last parameter is an array of values to pass to the function.

The difference between apply and call is the signature of the method.

const agentData = {
  id: 007,
  fullName: 'Not Set',
  // setUserName is a method on the agentData object
  setUserName: function (firstName, lastName)  {
    this.fullName = firstName + ' ' + lastName;
  }
}

function getUserInput(firstName, lastName, callback, callbackObj)  {
   if ((firstName.length > 3) && (lastName.length > 3)) {
    callback.apply (callbackObj, [firstName, lastName]);
  } else {
    console.log('data could not be saved.');
  }
}

The signature for getUserInput doesn’t change when we use apply instead of call. The results are identical.

getUserInput ('James', 'Bond', agentData.setUserName, agentData);

console.log (agentData.fullName); // James Bond
console.log (window.fullName); // Undefined

Try/Catch blocks

The second way to write synchronous code is to create try/catch blocks.

The idea behind try/catch blocks is that we need to run instructions in sequence but we must also do something if any of the commands fail.

We will use the following JSON for the example.

// data from the server
let json = '{"id":"007", "firstName":"James", "lastName": "Bond"}';

In the try/catch block below, we want to make sure that the data parse succeeds and do something if there is an error.

In this example, we just log a message and the error to console. In more complex examples the catch error may attempt connecting to the database again or ask the user to enter the data again if it wasn’t complete or well formed.

try {
  // convert the text representation to JS object
  let user = JSON.parse(json);
  // Log the results to console
  console.log (user.id); // 007
  console.log (user.firstName);  // James
  console.log (user.lastName); // Bond
} catch(err) {
  console.log ('I\'m sorry Dave, I can\'t do that ');
  console.log (err.name);
  console.log (err.message);
}

There is a third optional parameter, finally. It will happen regardless of whether the try block succeeds or the catch block is called. This gives the option of doing cleanup, closing database connections and doing other cleanup tasks your code needs to complete.

In the example below, the script will always log All Done, regardless if the JSON parsing was successful or not.

try {
  // convert the text representation to JS object
  let user = JSON.parse(json);
  // Log the results to console
  console.log (user.id); // 007
  console.log (user.firstName);  // James
  console.log (user.lastName); // Bond
} catch(err) {
  console.log ('I\'m sorry Dave, I can\'t do that ');
  console.log (err.name);
  console.log (err.message);
} finally {
  //This will always execute regardless
  console.log ('All Done');
}

Brief Introduction to Async Code

Async (short for asynchronous) code will execute without blocking rendering and return a value when the code in the async block finishes.

Contrast the definition of async with the synchronous code we’ve been working so far where all statements are executed in the order they appear.

The example below uses both sync (synchronous) and async (asynchronous) code to illustrate the difference. The console log statements outside of fetch will execute in the order they appear in the document.

console.log ('Starting');
fetch('https://s3-us-west-2.amazonaws.com/s.cdpn.io/32795/coffee2.png') // 1
  .then((response) => {
    if (response.ok) {
      console.log('It worked :)')
      return response.blob(); // 2
    } else {
      throw new Error('Network response was not ok.' );
    }
  })
  .then((myBlob) => {
    let objectURL = URL.createObjectURL(myBlob); // 3
    let myImage = document.createElement('img'); // 4
    myImage.src = objectURL; // 5
    document.body.appendChild(myImage); // 6
  })
  .catch((error) => {
    console.log(
      'There has been a problem with your fetch operation: ',
      error.message
    );
  });
console.log ('All done!');

The fetch function and it’s associated chain uses the Promise API (discussed in more detail in a later document) to get a resource from the network and perform one or more tasks. It won’t make the script wait until it’s done but will continue working in the background until it completes.

The async nature of the Fetch API produces unexpected results. The sequence of messages logged to console:

  1. Starting
  2. All done!
  3. It worked 🙂

The messages from both console.log calls outside fetch appear in their document order but the message signaling success doesn’t appear until the chain started with fetch completes.

Javascript is at its most basic a synchronous, blocking, single-threaded language; Only one operation can be in progress at a time.

Whether we want to run code synchronously or asynchronously will depend on what we’re trying to do.

There are times when we want things to load and happen right away. The callback for a click event handler is a good example.

If we’re running an expensive operation like querying a database and using the results to populate templates then we want to push this off the main thread and complete the task asynchronously.

Prototypal Inheritance and Classes

If you’re working with modern evergreen browsers, classes are the preferred way to create reusable code. They work in all modern browsers according to caniuse.com out of the box. Prototypal inheritance will work if you need to support older browsers.

Prototypal Inheritance

Before ES6 we created reusable content using functions to create objects and attaching the methods to the object’s prototype. This way every object that inherits from the object will also inherit the methods attached to the prototype.

In the example below the Person has the following properties: first, last, age, gender, and interests. all these properties are required for all Person objects.

It also defines two methods for the Person object with the syntax Person.prototype.methodName to create a function representing the method.

Both methods return alerts with strings that interpolate values using string concatenation to pull elements from the Person object to display it.

The interests attribute is an array of one or more elements. For these examples, I’ve made them arrays of one item.

The name attribute is an object made of two children: first and last. This is why the methods refer to this.name.first rather than this.first.

Be careful when using string interpolation that you match the quotation marks and that you escape quotation marks that are part of the sentence rather than the string. Look at how we escaped the apostrophe in I'm to make sure that it didn’t close the string and caused errors when we run the code.

function Person(first, last, age, gender, interests) {
  this.name = {
    first,
    last
  };
  this.age = age;
  this.gender = gender;
  this.interests = interests;
};

//
Person.prototype.greeting = function() {
  alert('Hi! I\'m ' + this.name.first + '.');
};

Person.prototype.farewell = function() {
  alert(this.name.first + ' has left the building. Bye for now!');
}

We can then create new People based on the Person object with their four attributes and getting the two methods we defined. We use the object.method syntax to use the methods defined in the object’s prototype.

const kirk = new Person('Jim', 'Kirk', 39, 'male', ['starships']);
const mccoy = new Person('Leonard', 'McCoy', 44 ['Medicine']);

kirk.greeting();
// Hi! I'm Jim.
mccoy.farewell();
// Leonard has left the building. Bye for now!

ES2015 Classes

ES2015 introduces classes to JavaScript as a way to give a cleaner syntax for reusable code. This syntax will be familiar if you’ve written code in C++ and Java. We’ll convert the Person and Teacher examples from prototypal inheritance to a Class.

The constructor element builds the function that represents our class, in our example Person. All person objects will have these items.

greeting() and farewell() are class methods. They are defined as part of the class but are not required like the items inside the constructor. We can have a person without greeting and farewell but we can not have a farewell or a greeting without a person.

Both class methods use string literals rather than string concatenation in the class methods to make string interpolation easier and the code easier to read.

class Person {
  constructor(first, last, age, gender, interests) {

    this.name = {
      first,
      last
    };

    this.age = age;
    this.gender = gender;
    this.interests = interests;
  }


  greeting() {
    alert(`Hi! I'm ${this.name.first}`);
  };

  farewell() {
    alert(`${this.name.first} has left the building. Bye for now!`);
  };
}

We then instantiate Person objects using the new Person() syntax and call methods using the object.method syntax.

let han = new Person('Han', 'Solo', 25, 'male', ['smuggling']);
han.greeting();
// Hi! I'm Han

let leia = new Person('Leia', 'Organa', 19, 'female' ['government']]);
leia.farewell();
// Leia has left the building. Bye for now

Under the hood, JavaScript engines will convert the classes into Prototypal Inheritance models. You don’t have to worry about it, it’s all done for you.

Inheritance: Creating Classes Based On Other Classes

We created a class to represent a Person. They have a series of attributes that are common to all people; Let’s say that we want to create a specialized type of Person: a Teacher.

To save ourselves the typing we’ll base our teacher class on the person we’ve already created. This is called creating a subclass or subclassing.

To create a subclass we use the extend keyword to tell Javascript the class we want to base our class on.

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    this.name = {
      first,
      last
    };

  this.age = age;
  this.gender = gender;
  this.interests = interests;
  // subject and grade are specific to Teacher
  this.subject = subject;
  this.grade = grade;
  }
}

We can make the code more readable by using the super() declaration as the first item in the class constructor. This will call the parent class’ constructor to take care of items defined there. We can further refine the Teacher example by calling super in the constructor; this will call Person’s constructor and set up its values.

We can also add additional items that are specific to the class we are creating like subject and grade in the Teacher example.

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    super(first, last, age, gender, interests);

    // subject and grade are specific to Teacher
    this.subject = subject;
    this.grade = grade;
  }
}

The teacher class uses values from Person and adds functionality that goes beyond the parent class. We didn’t write greeting() and farewell() methods for Teacher but they are available as part of Person, so we can still use them, we get them “for free” in Teacher objects.

let snape = new Teacher('Severus', 'Snape', 58, 'male', ['potions'], 'dark arts', 5);
snape.greeting(); // Hi! I'm Severus.
snape.farewell(); // Severus has left the building. Bye for now.

Like we did with Teachers, we could create other subclasses of person to make them more specialized without modifying the base person class.

Getters and Setters

There may be times when we want to change the values of an attribute in the classes we create or we don’t know what the final value of an attribute will be. Using the Teacher example, we may not know what subject the teacher teaches or the subject may change from the time we create the teacher object to the time we use it.

We can do it with getters and setters.

We’ll use the Teacher class we defined earlier and enhance it with getters and setters. The class starts the same than it was the last time we looked at it.

Getters and setters work in pairs. Getters return the current value of the variable and setters change the value of the variable to the ones it defines.

The modified Teacher class looks like this:

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    super(first, last, age, gender, interests);
    // subject and grade are specific to Teacher
    this._subject = subject;
    this.grade = grade;
  }

  get subject() {
    return this._subject;
  }

  set subject(newSubject) {
    this._subject = newSubject;
  }
}

In our class above we have a getter and setter for the subject property. We use _  to create a backing field to store our name property. Without this convention, we will get stack overflow errors every time we call get or set.

To show the current value of the _subject property of the snape object use snape._subject. To assign a new value to the _subject property use snape._subject="new value". The example below shows the two features in action:

// Check the default value
snape._subject // Returns "potions"

// Change the value
snape._subject="magic arts"
// Sets subject to "magic arts"

// Check it again and see if it matches the new value
snape._subject // Returns "magic arts"