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.
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:
-
Function Declaration: The function is declared as
async
and returnsPromise<void>
. While theasync
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. -
Promise Construction: We create a new Promise using the Promise constructor, which takes an executor function. This executor receives a
resolve
function (abbreviated asr
in the code) as its first parameter. -
setTimeout Integration: Inside the Promise executor, we call
setTimeout(r, millis)
. This schedules the resolve functionr
to be called aftermillis
milliseconds. The key insight is thatsetTimeout
expects a callback function as its first argument, andr
(the resolve function) perfectly fits this requirement. -
Execution Flow:
- When
sleep(millis)
is called, a new Promise is created immediately and returned setTimeout
schedules the resolve callback to run aftermillis
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
- When
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 EvaluatorExample 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
How does merge sort divide the problem into subproblems?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!