Tuesday, June 23, 2020

Published June 23, 2020 by with 3 comments

How Do I Sleep for a Specific Time in JavaScript?

For a more general question, how do you use async and await with nearly anything in JavaScript?
As a simple example, I'll walk through the title question. Say you want to do something, wait 4 seconds, then do something else. You naively try the following:

console.log(new Date());
setTimeout(function () {
  console.log(new Date());
}, 4000);
console.log(new Date());

If you try that, you'll immediately notice that the console.log after the setTimeout executes immediately and does not wait 4 seconds for the setTimeout. The issue is that the setTimeout is effectively saying 'in 4 seconds, execute this content' and then moving on.

A simple way to handle this is to have the timeout body resolve a promise. Then, you can just await that promise after, and you'll get the behavior you expect. Here's a simple example of that:

async function test() {
  console.log(new Date());
  promise = new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(new Date());
      resolve();
    }, 4000);
  });

  await promise;
  console.log(new Date());
}

Just wrap the setTimeout in a promise, resolve after doing the setTimeout stuff, and await before moving on. That last console.log will now execute after that setTimeout. Here is a working example of both to see this:

See the Pen await timeout demo by Robert Hamner (@rhamner) on CodePen.

For a related example...what if you want to run httprequests in a sequence instead of in parallel? Again, you naively try this and see that it doesn't work:

let req1 = new XMLHttpRequest();
req1.open("GET", "https://jsonplaceholder.typicode.com/todos/1", true);
req1.send(null);

let req2 = new XMLHttpRequest();
req2.open("GET", "https://jsonplaceholder.typicode.com/todos/1", true);
req2.send(null);

It's the same basic issue as before...nothing here tells the req2 code to wait for req1 to finish. To fix it, you can do something like this:

async function test() {
  let promise = new Promise(function (resolve, reject) {
    let req1 = new XMLHttpRequest();
    req1.open("GET", "https://jsonplaceholder.typicode.com/todos/1", true);
    req1.onreadystatechange = function () {
      if (req1.readyState === 4 && req1.status === 200) {
        resolve();
      }
    };
    req1.send(null);
  });

  await promise;

  let req2 = new XMLHttpRequest();
  req2.open("GET", "https://jsonplaceholder.typicode.com/todos/1", true);
  req2.send(null);
}

test();

Now, req1 is wrapped in a promise that only resolves when req1 gets a valid response, and the req2 code comes after an await for that promise (note this is poorly written...real code would handle other responses for example). Here's an example of these:

See the Pen await httprequest demo by Robert Hamner (@rhamner) on CodePen.

To see it, go to that code, turn on network recording in your browser, and run. You should see three simultaneous requests for '1', then a fourth one that comes after the third one finishes.

This technique can be used to order many things in JavaScript and is really flexible and simple.



      edit

3 comments: