In the next few lessons, you will simulate a sleep function with async functions to further understand the dynamics of async functions and promises.
For these lessons. you will need to set up a sandbox to experiment with the functions that will be provided. Do this locally on an index.html file with a link to a blank script.js file and serve that on a localhost server.
Ways to Build a Sleep Function in Python and JavaScript
You may have noticed that JavaScript does not have a sleep function like there is in Python, for example:
import time
print("Starting")
time.sleep(5)
print("5 seconds have passed")
Here, the time.sleep() function blocks normal execution for 5 seconds. It's a synchronous function that does not return until the 5 seconds have passed, effectively pausing the execution of the script.
In JavaScript, there is no default way to block normal execution for a period of time like this. The closest default function that JavaScript has is setTimeout():
console.log('Starting');
setTimeout(() => console.log('5 seconds have passed'), 5000);
This runs the arrow function callback after 5 seconds. While the effect of these two pieces of code is the similar, the mechanism by which they achieve this result is very different and highlights some of the main differences between the two languages.
setTimeout() is an asynchronous function (not an async function, though). It does not block normal execution. Instead, it schedules the callback to be executed after the specified number of milliseconds. This means that the script will continue to execute normally, and the callback will be executed after the specified number of milliseconds.
This asynchronous behavior explains why the following code will log Starting and then Done immediately, and then 5 seconds have passed after 5 seconds:
console.log('Starting');
setTimeout(() => console.log('5 seconds have passed'), 5000);
console.log('Done');
/* OUTPUT
Starting
Done
5 seconds have passed
*/
The console.log('Done') line is executed immediately after the setTimeout() is called. All calling setTimeout() does is schedule the callback to be executed after the wait provided as an argument. It does not wait for the callback to be executed before continuing with the rest of the script.
With Python, it's straightforward to run operations with very specific timing:
import time
print("Starting")
time.sleep(5)
print("5 seconds have passed")
time.sleep(5)
print("10 seconds have passed")
time.sleep(5)
print("15 seconds have passed")
With JavaScript and setTimeout(), however, this starts to get into pyramid of doom territory:
console.log('Starting');
setTimeout(() => {
console.log('5 seconds have passed');
setTimeout(() => {
console.log('10 seconds have passed');
setTimeout(() => {
console.log('15 seconds have passed');
}, 5000);
}, 5000);
}, 5000);
This is not very readable and can get very confusing very quickly. Hence the name "pyramid of doom". Ideally, you'd like to be able to write something resembling the Python code above.
Use Async Functions to Avoid Callback Nesting
To solve the nested look, you can use async functions. It starts with a utility function called sleep():
function sleep(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
This is a function designed to be used in an async function, it:
- Takes an argument of the number of
millisecondsto sleep - It creates a new promise whose callback:
- Only uses the
resolvemethod - Calls
setTimeout()to callresolve()after the number ofmilliseconds
- Only uses the
Within an async function, this allows you to call sleep() very similarly to how you would in Python:
async function timeSensitiveProcess() {
console.log('Starting');
await sleep(5000);
console.log('Mid way');
await sleep(5000);
console.log('Done');
}
timeSensitiveProcess();
This looks a lot like the synchronous Python code!
Try It Yourself
Make sure you have all this code in a script.js file and experiment with it.
You'll see that this script will log Starting and then wait 5 seconds, log Mid way, wait another five seconds, and then log Done. Note the await before every sleep() which tells the async function that until the expression to its right has returned a resolved promise, it should not execute any of the following lines.
A sleep function like this can be useful to simulate long-running processes when testing. Though as far as possible, you should test with the real thing.
How the JavaScript sleep differs from Python where you have a sleep function that literally pauses execution, is that you can still process events while the async function is "sleeping". So JavaScript is not really sleeping at all!
Event Handling During JavaScript's Sleep Function
Here is some basic JavaScript that will create a button that will log something to the console on every button press:
function main() {
const button = document.createElement('button');
button.addEventListener('click', () => console.log('click'));
button.innerText = 'Click me';
document.body.append(button);
}
main();
Now, if you run the timeSensitiveProcess() done in the last section after calling main() and try pressing the button a bunch of times, you'll see that even though the timeSensitiveProcess() takes a long time, at no point does it block the reception and logging of events!
This shows that even though you have called the function sleep() it is not like Python's sleep. JavaScript can attend to other things while the async function is awaiting the resolution of the sleep() function!
Summary: How to Create a JavaScript Sleep Function
In this lesson, you've:
- Seen that JavaScript doesn't have a built-in
sleepfunction like Python, instead relying onsetTimeout()for delays. - Noticed the contrast as
setTimeout()doesn't block execution, revealing how JavaScript handles asynchronous operations. - Discovered the so-called "pyramid of doom" when multiple
setTimeout()calls are nested, leading to less readable code. - Learned how to create a
sleepfunction in JavaScript using Promises and thesetTimeout()function to achieve a delay similar to Python'ssleep. - Seen how
async/awaitcan make asynchronous JavaScript code appear more sequential and readable. - Understood that
awaitpauses the execution of anasyncfunction until the Promise is resolved, streamlining process simulations.