Delay, Sleep, Pause, & Wait in JavaScript

Many programming languages have a sleep function that will delay a program’s execution for a given number of seconds. This functionality is absent from JavaScript, however, owing to its asynchronous nature. In this article, we’ll look briefly at why this might be, then how we can implement a sleep function ourselves.

We’ve rewritten this popular guide from scratch to provide the best and most up-to-date advice. This article was updated in November 2019.

Understanding JavaScript’s Execution Model

Before we get going, it’s important to make sure we understand JavaScript’s execution model correctly.

Consider the following Ruby code:

require 'net/http'
require 'json'

url = 'https://api.github.com/users/jameshibbard'
uri = URI(url)
response = JSON.parse(Net::HTTP.get(uri))
puts response['public_repos']
puts "Hello!"

As one might expect, this code makes a request to the GitHub API to fetch my user data. It then parses the response, outputs the number of public repos attributed to my GitHub account and finally prints “Hello!” to the screen. Execution goes from top to bottom.

Contrast that with the equivalent JavaScript version:

fetch('https://api.github.com/users/jameshibbard')
  .then(res => res.json())
  .then(json => console.log(json.public_repos));
console.log("Hello!");

If you run this code, it will output “Hello!” to the screen, then the number of public repos attributed to my GitHub account.

This is because fetching data from an API is an asynchronous operation in JavaScript. The JavaScript interpreter will encounter the fetch command and dispatch the request. It will not, however, wait for the request to complete. Rather, it will continue on its way, output “Hello!” to the console, then when the request returns a couple of hundred milliseconds later, it will output the number of repos.

If any of this is news to you, you should watch this excellent conference talk: What the heck is the event loop anyway?.

You Might Not Actually Need a Sleep Function

Now that we have a better understanding of JavaScript’s execution model, let’s have a look at how JavaScript handles delays and asynchronous operations.

Create a Simple Delay Using setTimeout

The standard way of creating a delay in JavaScript is to use its setTimeout method. For example:

console.log("Hello");
setTimeout(() => {  console.log("World!"); }, 2000);

This would log “Hello” to the console, then after two seconds “World!” And in many cases, this is enough: do something, wait, then do something else. Sorted!

However, please be aware that setTimeout is an asynchronous method. Try altering the previous code like so:

console.log("Hello");
setTimeout(() => { console.log("World!"); }, 2000);
console.log("Goodbye!");

It will log:

Hello
Goodbye!
World!

Waiting for Things with setTimeout

It’s also possible to use setTimeout (or its cousin setInterval) to keep JavaScript waiting until a condition is met. For example, here’s how you might use setTimeout to wait for a certain element to appear on a web page:

function pollDOM () {
  const el = document.querySelector('my-element');

  if (el.length) {
    // Do something with el
  } else {
    setTimeout(pollDOM, 300); // try again in 300 milliseconds
  }
}

pollDOM();

This assumes the element will turn up at some point. If you’re not sure that’s the case, you’ll need to look at canceling the timer (using clearTimeout or clearInterval).

If you’d like to find out more about JavaScript’s setTimeout method, please consult our tutorial which has plenty of examples to get you going.

Flow Control in Modern JavaScript

It’s often the case when writing JavaScript that we need to wait for something to happen (for example, data to be fetched from an API), then do something in response (such as update the UI to display the data).

The example above uses an anonymous callback function for this purpose, but if you need to wait for multiple things to happen, the syntax quickly gets pretty gnarly and you end up in callback hell.

Luckily, the language has evolved considerably over the past few years and now offers us new constructs to avoid this.

For example, using async await we can rewrite the initial code to fetch information from the GitHub API:

(async () => {
  const res = await fetch(`https://api.github.com/users/jameshibbard`);
  const json = await res.json();
  console.log(json.public_repos);
  console.log("Hello!");
})();

Now the code executes from top to bottom. The JavaScript interpreter waits for the network request to complete and the number of public repos is logged first, then the “Hello!” message.

If this is more the sort of thing you’re trying to accomplish, I encourage you to read our article Flow Control in Modern JS: Callbacks to Promises to Async/Await.

Bringing Sleep to Native JavaScript

If you’re still with me, then I guess you’re pretty set on blocking that execution thread and making JavaScript wait it out.

Here’s how you might do that:

function sleep(milliseconds) {
  const date = Date.now();
  let currentDate = null;
  do {
    currentDate = Date.now();
  } while (currentDate - date < milliseconds);
}

console.log("Hello");
sleep(2000);
console.log("World!");

As expected, this will log “Hello”, pause for two seconds, then log “World!”

It works by using the Date.now method to get the number of milliseconds that have elapsed since January 1, 1970 and assigning that value to a date variable. It then creates an empty currentDate variable, before entering a do ... while loop. In the loop it repeatedly gets the number of milliseconds which have elapsed since January 1, 1970 and assigns the value to the previously declared currentDate variable. The loop will keep going while the difference between date and currentDate is less than the desired delay in milliseconds.

Job done, right? Well not quite …

A Better Sleep Function

Maybe this code does exactly what you’re hoping for, but be aware, it has a large disadvantage: the loop will block JavaScript’s execution thread and ensure that nobody can interact with your program until it finishes. If you need a large delay, there’s a chance that it may even crash things altogether.

So what to do?

Well, it’s also possible to combine the techniques learned earlier in the article to make a less intrusive sleep function:

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

console.log("Hello");
sleep(2000).then(() => { console.log("World!"); });

This code will log “Hello”, wait for two seconds, then log “World!” Under the hood we’re using the setTimeout method to resolve a Promise after a given number of milliseconds.

Notice that we need to use a then callback to make sure the second message is logged with a delay. We can also chain more callbacks onto the first:

console.log("Hello");
sleep(2000)
  .then(() => { console.log("World!"); })
  .then(() => {
    sleep(2000)
      .then(() => { console.log("Goodbye!"); })
    });

This works, but looks ugly. We can pretty it up using async ... await:

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function delayedGreeting() {
  console.log("Hello");
  await sleep(2000);
  console.log("World!");
  await sleep(2000);
  console.log("Goodbye!");
}

delayedGreeting();

This looks nicer, but means that whatever code is using the sleep function needs to be marked as async.

Click here.

Of course, both of these methods still have the disadvantage (or feature) that they do not pause the entire program execution. Only your function sleeps:

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function delayedGreeting() {
  console.log("Hello");
  await sleep(2000);
  console.log("World!");
}

delayedGreeting();
console.log("Goodbye!");

The code above logs the following:

Hello
Goodbye!
World!

Conclusion

Timing issues in JavaScript are the cause of many a developer headache, and how you deal with them depends on what you’re trying to achieve.

Although a sleep function is present in many other languages, I’d encourage you to embrace JavaScript’s asynchronous nature and try not to fight the language. It’s actually quite nice when you get used to it.

#javascript #programming

Delay, Sleep, Pause, & Wait in JavaScript
7.35 GEEK