Saturday, May 22, 2021

How to implement sleep function in JavaScript

Most popular mainstream languages have a sleep functionality that allows for the pausing of code execution for a specified amount of time. Not surprising this functionality is often implemented via a function usually named sleep. For example:

// ruby
sleep(time_secs)
// python
import time
time.sleep(time_secs)
// Java
Thread.sleep(time_milli_secs)
// Perl
sleep(time_secs)

JavaScript does not have a similar sleep function, although it has the setTimeout function which can be used to achieve a similar purpose. The only problem is, using setTimeout is not as convenient as one would like. This is due to the fact that setTimeout is an asynchronous function, hence one would have to continue the execution of the code, in the callback passed to it, after the elapsed time.

console.log("Executing code..."); 
setTimeout(() => { 
console.log("continue after pause"); 
}, 3000)

This is not convenient.

A more convenient option would be to have something similar to:

console.log("Executing code..."); 
// sleep for some time
// sleep()
console.log("continue after pause");

Unfortunately, this does not exist natively in JavaScript. But this is not to say we can't implement one!

We can implement such a sleep function by combining the capabilities of Promises and Async/Await in JavaScript. For a quick recap of the syntax involved, see Callback, Promise and Async/Await by Example in JavaScript.

The implementation of such a sleep function, involves "Promisifying" the setTimeout function, and then using the Async/Await syntax to provide a synchronous feel to a rather asynchronous function. It looks this:

function sleep(milliseconds) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve({})
        }, milliseconds);
    })
}
In a less verbose syntax this would be:
(milliseconds: number) => new Promise(resolve => setTimeout(resolve, milliseconds))

Usage would have to be within a function marked as async:

// definition
function sleep(milliseconds) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve({})
        }, milliseconds);
    })
}

//usage
async function doStuff() {
    console.log("start")
    await sleep(3000)
    console.log("continue")
}

doStuff()

The requirement to put the code in an async function is because, as of now, browsers and NodeJS do not support top-level async functions. Attempting to have the code used in the browser, outside of an async function, would lead to an error similar to this:

Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules

Attempting to run it in Node, would also lead to an error:

SyntaxError: await is only valid in async functions and the top level bodies of modules

Deno on the other hand, already supports top-level async functions, hence with Deno, the code can be used right away:

// definition
function sleep(milliseconds) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve({})
        }, milliseconds);
    })
}

//usage
console.log("start")
await sleep(3000)
console.log("continue")

Assuming the above code is in a file named script.js it can be executed using Deno as follows:

$> deno run script.js 
start
// pause for 3 seconds
continue
And there you have it: an implementation of a sleep function in JavaScript, which has a synchronous feel to it's usage. Definitely more convenient than using setTimeout directly.
Are you a developer coming to typescript from other programming languages? Then you will find my upcoming book useful: TypeScript Beyond The Basics. Sign up here to be notified when it is ready.

No comments: