2621. Sleep


Problem Description

The problem requires the creation of an asynchronous function in TypeScript that simulates a sleep or delay for a specified number of milliseconds given by the input parameter 'millis'. The 'millis' is a positive integer representing the time to pause execution of further code. Upon completing the specified pause duration, the function should be able to resume operation although it doesn't need to return any specific value or perform any particular action after the wait is over. The execution should move on, and that is the only requirement.

Intuition

The solution is straightforward due to the nature of JavaScript and TypeScript's handling of asynchronous operations via the Promises API and the built-in setTimeout function.

setTimeout is used to delay the execution of a function by a certain number of milliseconds. It is a non-blocking operation, meaning it allows the JavaScript runtime to continue executing other tasks in the event loop while waiting for the timeout to complete.

By wrapping setTimeout in a Promise, we are creating a promise that will resolve after a set amount of time, thus introducing a pause or sleep effect. This can be done by passing a resolve function r to the promise constructor, and then passing that resolve function to setTimeout. The setTimeout function calls r after the specified millis, signaling to the Promise that it is complete. Since we do not care about the value with which the promise resolves, r can be called with no arguments.

When we call the sleep function with await in an async function or use .then() after its invocation, the JavaScript runtime will halt the execution of that function, thus effectively "sleeping", until the set amount of time has passed.

The simplicity of this solution makes it an elegant and widely used approach to introduce a deliberate pause in asynchronous execution without blocking the JavaScript event loop.

Solution Approach

The solution takes advantage of the JavaScript event loop and the Promise object to effectively pause execution asynchronously. There are no complex algorithms, data structures, or patterns needed, as the solution leverages existing JavaScript/TypeScript functionalities to accomplish the 'sleep' behavior.

Here's a step-by-step explanation of how the provided solution accomplishes the delay:

  • An asynchronous function sleep is declared that takes a single parameter, millis, which is a number. This function returns a Promise that resolves to void, since the function does not need to return a value once the delay is complete.

  • Inside the function, a new Promise is constructed. The constructor of the Promise takes an executor function, which has the resolve function r as an argument.

  • Within the executor function, we then call setTimeout. This is a built-in function that schedules another function to be run after a given number of milliseconds. In this case, our scheduled function is simply the resolve function r, which will be called after millis milliseconds.

  • By calling the resolve function r after the delay provided by setTimeout, we tell the Promise that it's okay to continue with the next tasks. Since there's no need for a specific resolve value, r is called without any arguments.

  • When the sleep function is used, the caller should either await it (if within an async function) or attach a .then() method to handle the code that should run after the delay. The event loop is not blocked during this delay, allowing other asynchronous operations to continue.

  • Thus, once the set time has elapsed, the function within setTimeout (in this case, the resolve function r) is added to the call stack and, if the call stack is clear, is executed, resolving the promise and effectively "waking" the function out of sleep.

No data structures are used in this solution. The asynchronous nature of the Promise, along with the setTimeout function, provide all the required functionality. This pattern of using setTimeout within a Promise is a common approach to introduce asynchronous delays in JavaScript and TypeScript codebases.

💪
Level Up Your
Algo Skills

Example Walkthrough

Let's say you want to create a simple application that sends a "hello" message to the console, waits for 2 seconds, and then sends a "world" message. Here's a step-by-step walkthrough of how you might use the sleep function to achieve this:

  1. Start by declaring an async function named greet where the sleep function will be used. This is your main function.

  2. Inside the greet function, first output "hello" to the console using console.log("hello").

  3. Directly after printing "hello", call the sleep function with 2000 milliseconds as the argument to create a 2-second delay. This is written as await sleep(2000);.

  4. After the sleep function call, write another console.log() statement to output "world" to the console.

  5. Finally, call the greet function to run the complete sequence.

Here's how the code might look given the provided solution approach:

1// Declaration of the sleep function
2async function sleep(millis: number): Promise<void> {
3  return new Promise(r => setTimeout(r, millis));
4}
5
6// Declaration of the async function that uses sleep
7async function greet() {
8  console.log("hello"); // Step 2: Output "hello"
9  await sleep(2000);    // Step 3: Wait for 2 seconds
10  console.log("world"); // Step 4: Output "world" after the wait
11}
12
13// Execute the function
14greet();

When you run this code, you'll see "hello" printed to the console instantly. The program will then pause for 2 seconds, after which "world" will be printed to the console. This demonstrates the non-blocking behavior of the sleep function, as it allows the "hello" message to be processed while the event loop handles other tasks during the 2-second wait before "world" is printed out.

Python Solution

1import asyncio
2
3# This function creates a coroutine that completes after a specified number of milliseconds.
4# It uses the asyncio.sleep function internally to delay completion.
5# 
6# @param millis: The number of milliseconds to wait before completing.
7# @return: A coroutine that completes after the specified delay.
8
9async def sleep(millis: int) -> None:
10    await asyncio.sleep(millis / 1000)  # asyncio.sleep expects seconds, so convert milliseconds to seconds.
11
12# Example usage of the sleep function. It logs the elapsed time (in milliseconds) to the console
13# after the sleep coroutine delay has completed.
14#
15# Usage:
16# asyncio.run(main())
17
18async def main():
19    # Initialize a variable to track the start time.
20    start_time = asyncio.get_event_loop().time() * 1000 # start_time is set in milliseconds
21
22    # Call the sleep coroutine with a delay of 100 milliseconds.
23    await sleep(100)
24
25    # Calculate the elapsed time by subtracting the start time from the current time.
26    elapsed_time = asyncio.get_event_loop().time() * 1000 - start_time  # Convert loop time to milliseconds
27
28    # Log the elapsed time to the console.
29    print(elapsed_time)  # This will print a value close to 100.
30
31# Run the main function if this script is executed
32if __name__ == '__main__':
33    asyncio.run(main())
34

Java Solution

1import java.util.concurrent.CompletableFuture;
2import java.util.concurrent.TimeUnit;
3
4// This class contains methods for asynchronous operations.
5public class AsyncHelper {
6
7    /**
8     * This method creates a CompletableFuture that completes after a specified number of milliseconds.
9     * It uses the scheduled executor internally to delay the completion of the CompletableFuture.
10     *
11     * @param millis The number of milliseconds to wait before completing the CompletableFuture.
12     * @return A CompletableFuture that completes after the specified delay.
13     */
14    public CompletableFuture<Void> sleep(long millis) {
15        return CompletableFuture.runAsync(() -> {
16            try {
17                TimeUnit.MILLISECONDS.sleep(millis);
18            } catch (InterruptedException e) {
19                Thread.currentThread().interrupt();
20            }
21        });
22    }
23}
24
25// The main class to run the example usage.
26public class SleepExample {
27
28    public static void main(String[] args) {
29        // Create an instance of AsyncHelper to use the sleep method.
30        AsyncHelper asyncHelper = new AsyncHelper();
31
32        // Initialize a variable to track the start time.
33        long startTime = System.currentTimeMillis();
34
35        // Call the sleep method with a delay of 100 milliseconds.
36        asyncHelper.sleep(100).thenRun(() -> {
37            // Calculate the elapsed time by subtracting the start time from the current time.
38            long elapsedTime = System.currentTimeMillis() - startTime;
39
40            // Log the elapsed time to the console.
41            System.out.println(elapsedTime); // This will print a value close to 100.
42        });
43    }
44}
45

C++ Solution

1#include <iostream>
2#include <chrono>
3#include <thread>
4
5// This function pauses execution for a specified number of milliseconds.
6//
7// @param millis The number of milliseconds to wait before resuming execution.
8void sleep(int millis) {
9    std::this_thread::sleep_for(std::chrono::milliseconds(millis));
10}
11
12// Example usage of the sleep function. It logs the elapsed time (in milliseconds) to the console
13// after the sleep function delay has completed.
14int main() {
15    // Initialize a variable to track the start time.
16    auto start_time = std::chrono::high_resolution_clock::now();
17
18    // Call the sleep function with a delay of 100 milliseconds.
19    sleep(100);
20
21    // Calculate the elapsed time by subtracting the start time from the current time.
22    auto end_time = std::chrono::high_resolution_clock::now();
23    auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
24  
25    // Log the elapsed time to the console.
26    std::cout << elapsed_time << std::endl; // This will print a value close to 100.
27
28    return 0;
29}
30

Typescript Solution

1// This function creates a Promise that resolves after a specified number of milliseconds.
2// It uses the setTimeout function internally to delay the resolution of the Promise.
3// 
4// @param millis The number of milliseconds to wait before resolving the Promise.
5// @returns A promise that resolves after the specified delay.
6async function sleep(millis: number): Promise<void> {
7    return new Promise(resolve => setTimeout(resolve, millis));
8}
9
10// Example usage of the sleep function. It logs the elapsed time (in milliseconds) to the console
11// after the sleep function delay has completed.
12//
13// Usage:
14// let elapsedTime = Date.now();
15// sleep(100).then(() => console.log(Date.now() - elapsedTime)); // Prints approximately 100
16
17// Initialize a variable to track the start time.
18let startTime: number = Date.now();
19
20// Call the sleep function with a delay of 100 milliseconds.
21sleep(100).then(() => {
22    // Calculate the elapsed time by subtracting the start time from the current time.
23    let elapsedTime: number = Date.now() - startTime;
24  
25    // Log the elapsed time to the console.
26    console.log(elapsedTime); // This will print a value close to 100.
27});
28

Time and Space Complexity

Time Complexity: The time complexity of the sleep function is O(1). This is because scheduling a timeout using setTimeout is an API call that enqueues a callback to be executed after a minimum delay. The complexity of the JavaScript code itself is constant, regardless of the size of millis.

Space Complexity: The space complexity of the sleep function is also O(1). The function creates a new Promise and a single timer internally, which use a constant amount of space. No additional space that grows with the input size is used.

😈
Become an
Algo Monster

Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫