Facebook Pixel

2621. Sleep

Problem Description

This problem asks you to create an asynchronous function called sleep that pauses execution for a specified number of milliseconds.

The function should:

  • Accept a parameter millis which is a positive integer representing the number of milliseconds to wait
  • Be asynchronous (return a Promise)
  • Pause/delay for the specified duration before resolving
  • Can resolve with any value (or no value)

The implementation uses JavaScript's setTimeout function wrapped in a Promise. When setTimeout completes after millis milliseconds, it calls the resolve function r, which fulfills the Promise and allows the code to continue.

For example, if you call sleep(100), the function will pause for approximately 100 milliseconds before continuing. The problem notes that small variations in the actual sleep duration are acceptable due to the nature of JavaScript's event loop and timer precision.

The usage pattern is:

sleep(100).then(() => {
    // This code runs after ~100ms delay
});

Or with async/await:

await sleep(100);
// This code runs after ~100ms delay

This is a fundamental utility function often used in asynchronous programming for introducing delays, timing operations, or simulating asynchronous behavior in tests.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

To create a delay in JavaScript, we need to work with asynchronous operations since JavaScript is single-threaded and blocking the main thread would freeze the entire application. The natural choice is setTimeout, which schedules a callback to run after a specified delay.

However, setTimeout uses a callback-based pattern, while modern JavaScript favors Promises and async/await for better readability and error handling. We need to bridge this gap by wrapping setTimeout in a Promise.

The key insight is that setTimeout accepts a callback function that gets executed after the delay. By passing the Promise's resolve function directly as this callback, we create a Promise that automatically resolves after the specified time:

new Promise(resolve => setTimeout(resolve, millis))

When setTimeout fires after millis milliseconds, it calls resolve, which fulfills the Promise. This allows the sleep function to be used with await or .then(), integrating seamlessly with async/await patterns.

The elegance of this solution comes from recognizing that we don't need to pass any value when resolving - we just need the Promise to complete after the delay. The shortened syntax r => setTimeout(r, millis) takes advantage of the fact that setTimeout returns a timer ID (which we don't need) and we only care about the side effect of scheduling the resolution.

This pattern transforms the callback-based timer API into a Promise-based one, making it composable with other asynchronous operations and allowing for cleaner, more readable code when introducing delays in async functions.

Solution Approach

The implementation is straightforward and consists of a single-line function body that combines Promise construction with the setTimeout API:

async function sleep(millis: number): Promise<void> {
    return new Promise(r => setTimeout(r, millis));
}

Let's break down the implementation step by step:

  1. Function Declaration: The function is declared as async and returns Promise<void>. While the async keyword isn't strictly necessary here (since we're explicitly returning a Promise), it makes the function's asynchronous nature clear and ensures type consistency.

  2. Promise Construction: We create a new Promise using the Promise constructor, which takes an executor function. This executor receives a resolve function (abbreviated as r in the code) as its first parameter.

  3. setTimeout Integration: Inside the Promise executor, we call setTimeout(r, millis). This schedules the resolve function r to be called after millis milliseconds. The key insight is that setTimeout expects a callback function as its first argument, and r (the resolve function) perfectly fits this requirement.

  4. Execution Flow:

    • When sleep(millis) is called, a new Promise is created immediately and returned
    • setTimeout schedules the resolve callback to run after millis milliseconds
    • The calling code can either await this Promise or use .then() to continue after the delay
    • After the specified time elapses, setTimeout invokes the resolve function
    • The Promise transitions from pending to resolved state
    • Any code waiting on this Promise continues execution

The beauty of this implementation lies in its simplicity - no intermediate variables, no explicit value passing, just a direct mapping between the callback-based setTimeout and the Promise-based async pattern. The function effectively converts time-based delays into awaitable Promises, enabling clean asynchronous flow control.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example where we use sleep(150) to create a 150ms delay:

Step 1: Function Call

const delayPromise = sleep(150);

When we call sleep(150), the function immediately creates and returns a new Promise. At this point, the Promise is in a "pending" state.

Step 2: Promise Construction Inside the function, this happens:

new Promise(r => setTimeout(r, 150))

The Promise constructor receives an executor function. This executor is called immediately with a resolve function (abbreviated as r).

Step 3: Timer Scheduling setTimeout(r, 150) is executed, which:

  • Schedules the resolve function r to be called after 150 milliseconds
  • Returns immediately (non-blocking)
  • The Promise remains in "pending" state

Step 4: Waiting Period For the next 150ms, the JavaScript event loop continues processing other tasks. Our Promise is still pending, and any code awaiting it is paused.

Step 5: Timer Fires After approximately 150ms, the JavaScript runtime's timer mechanism triggers and calls the resolve function r(). This transitions the Promise from "pending" to "resolved" state.

Step 6: Continuation If we used await:

console.log("Start");
await sleep(150);
console.log("End");  // This prints ~150ms after "Start"

Or with .then():

console.log("Start");
sleep(150).then(() => {
    console.log("End");  // This prints ~150ms after "Start"
});

The key insight is that setTimeout needs a callback function, and the Promise's resolve function serves perfectly as that callback. When the timer expires, calling resolve fulfills the Promise, allowing async code to continue.

Solution Implementation

1import asyncio
2import time
3
4async def sleep(millis: int) -> None:
5    """
6    Asynchronously pauses execution for a specified number of milliseconds
7  
8    Args:
9        millis: The number of milliseconds to sleep
10  
11    Returns:
12        None after the specified delay
13    """
14    # Convert milliseconds to seconds (asyncio.sleep expects seconds)
15    seconds = millis / 1000
16  
17    # Asynchronously sleep for the specified duration
18    await asyncio.sleep(seconds)
19
20# Example usage:
21# import asyncio
22# import time
23# 
24# async def main():
25#     t = time.time() * 1000  # Convert to milliseconds
26#     await sleep(100)
27#     print(int(time.time() * 1000 - t))  # Should print approximately 100
28# 
29# asyncio.run(main())
30
1import java.util.concurrent.CompletableFuture;
2import java.util.concurrent.TimeUnit;
3
4/**
5 * Utility class for asynchronous sleep operations
6 */
7public class AsyncSleepUtil {
8  
9    /**
10     * Asynchronously pauses execution for a specified number of milliseconds
11     * @param millis The number of milliseconds to sleep
12     * @return A CompletableFuture that completes after the specified delay
13     */
14    public static CompletableFuture<Void> sleep(long millis) {
15        // Create a CompletableFuture that completes after the specified timeout
16        return CompletableFuture.runAsync(() -> {
17            try {
18                // Pause the current thread for the specified duration
19                Thread.sleep(millis);
20            } catch (InterruptedException e) {
21                // Restore the interrupted status
22                Thread.currentThread().interrupt();
23                // Propagate the exception
24                throw new RuntimeException(e);
25            }
26        });
27    }
28  
29    /**
30     * Alternative implementation using CompletableFuture.delayedExecutor
31     * @param millis The number of milliseconds to sleep
32     * @return A CompletableFuture that completes after the specified delay
33     */
34    public static CompletableFuture<Void> sleepAlternative(long millis) {
35        // Use delayedExecutor to schedule completion after the specified delay
36        return CompletableFuture.runAsync(
37            () -> {}, // Empty runnable as we only need the delay
38            CompletableFuture.delayedExecutor(millis, TimeUnit.MILLISECONDS)
39        );
40    }
41  
42    /**
43     * Example usage demonstrating the sleep functionality
44     */
45    public static void main(String[] args) {
46        // Record the start time
47        long startTime = System.currentTimeMillis();
48      
49        // Call sleep and handle completion
50        sleep(100).thenRun(() -> {
51            // Calculate and print elapsed time
52            long elapsedTime = System.currentTimeMillis() - startTime;
53            System.out.println(elapsedTime); // Should print approximately 100
54        });
55      
56        // Keep main thread alive to see the result
57        try {
58            Thread.sleep(200);
59        } catch (InterruptedException e) {
60            Thread.currentThread().interrupt();
61        }
62    }
63}
64
1#include <chrono>
2#include <thread>
3#include <future>
4#include <iostream>
5
6/**
7 * Asynchronously pauses execution for a specified number of milliseconds
8 * @param millis - The number of milliseconds to sleep
9 * @returns A future that completes after the specified delay
10 */
11std::future<void> sleep(int millis) {
12    // Create an async task that sleeps for the specified duration
13    return std::async(std::launch::async, [millis]() {
14        // Convert milliseconds to chrono duration and sleep
15        std::this_thread::sleep_for(std::chrono::milliseconds(millis));
16    });
17}
18
19/**
20 * Example usage:
21 * auto start = std::chrono::high_resolution_clock::now();
22 * sleep(100).wait();
23 * auto end = std::chrono::high_resolution_clock::now();
24 * auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
25 * std::cout << duration.count() << std::endl; // ~100
26 */
27
1/**
2 * Asynchronously pauses execution for a specified number of milliseconds
3 * @param millis - The number of milliseconds to sleep
4 * @returns A Promise that resolves after the specified delay
5 */
6async function sleep(millis: number): Promise<void> {
7    // Create a Promise that resolves after the specified timeout
8    return new Promise(resolve => setTimeout(resolve, millis));
9}
10
11/**
12 * Example usage:
13 * let t = Date.now()
14 * sleep(100).then(() => console.log(Date.now() - t)) // 100
15 */
16

Time and Space Complexity

Time Complexity: O(1)

The sleep function performs a constant amount of work regardless of the input value millis. It creates a single Promise and registers a timer callback with setTimeout. The actual waiting time (millis milliseconds) is handled asynchronously by the JavaScript runtime and doesn't affect the computational complexity of the function itself. The function returns immediately after setting up the timer.

Space Complexity: O(1)

The function allocates a fixed amount of memory:

  • One Promise object
  • One timer callback function r (the resolve function)
  • One timer entry in the event loop's timer queue

These allocations don't scale with the input millis value, resulting in constant space usage.

Common Pitfalls

1. Confusing JavaScript and Python Async Patterns

A critical pitfall when implementing sleep functions across languages is misunderstanding the fundamental differences between JavaScript's Promise-based and Python's coroutine-based async models.

JavaScript Pitfall:

// WRONG - This doesn't actually pause
function sleep(millis) {
    setTimeout(() => {}, millis);  // This returns undefined immediately!
}

// Code continues immediately, no pause occurs
sleep(1000);
console.log("This prints immediately");

Solution: Always return a Promise that resolves after the timeout:

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

2. Unit Confusion Between Milliseconds and Seconds

Python Pitfall:

async def sleep(millis: int) -> None:
    # WRONG - asyncio.sleep expects seconds, not milliseconds!
    await asyncio.sleep(millis)  # This would sleep for 1000 seconds instead of 1 second

Solution: Always convert milliseconds to seconds in Python:

async def sleep(millis: int) -> None:
    await asyncio.sleep(millis / 1000)  # Correct conversion

3. Blocking vs Non-Blocking Sleep

Python Pitfall:

import time

async def sleep(millis: int) -> None:
    # WRONG - This blocks the entire event loop!
    time.sleep(millis / 1000)  # Blocks everything, defeats async purpose

Solution: Use asyncio.sleep() for async contexts:

async def sleep(millis: int) -> None:
    await asyncio.sleep(millis / 1000)  # Non-blocking, allows other tasks to run

4. Forgetting to Await or Handle the Promise

JavaScript Pitfall:

async function doWork() {
    sleep(1000);  // WRONG - Promise is created but not awaited
    console.log("This runs immediately, not after 1 second");
}

Solution: Always await or chain with .then():

async function doWork() {
    await sleep(1000);  // Correct
    console.log("This runs after 1 second");
}

5. Integer Overflow or Negative Values

Common Pitfall:

// What happens with invalid input?
sleep(-100);  // Negative delay
sleep(2**32); // Extremely large value

Solution: Add input validation:

async function sleep(millis) {
    if (millis < 0) millis = 0;  // Handle negative values
    if (millis > 2147483647) millis = 2147483647;  // Cap at max 32-bit int
    return new Promise(r => setTimeout(r, millis));
}

6. Precision Expectations

Pitfall: Expecting exact millisecond precision:

// WRONG expectation
const start = Date.now();
await sleep(10);
const elapsed = Date.now() - start;
console.assert(elapsed === 10);  // This might fail! Could be 11, 12, or more

Solution: Allow for timing variance:

const start = Date.now();
await sleep(10);
const elapsed = Date.now() - start;
console.assert(elapsed >= 10 && elapsed < 20);  // Allow reasonable variance
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How does merge sort divide the problem into subproblems?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More