2821. Delay the Resolution of Each Promise
Problem Description
The problem involves an array called functions
that contains several functions. Each function, when called, will return a promise. A promise in JavaScript is an object representing the completion (or failure) of an asynchronous operation. Along with the functions
array, you are also given a number ms
, which represents the number of milliseconds of delay to introduce before each promise resolves.
Your task is to return a new array of functions so that when each function is called, it will return a promise that only resolves after waiting for the duration specified by ms
. This delay should be applied individually to each of the promises returned by the functions in the new array. Each promise must preserve the order in which the original functions from the functions
array were called.
The goal is to create a new array of functions in such a way that if you call the functions in sequence, each function will delay its execution by the specified ms
milliseconds before resolving its promise, ensuring that the asynchronous operations are executed with a consistent delay between them.
Intuition
To solve this problem, we leverage the map
function in JavaScript, which allows us to transform each element in an array using a transformation function. In this context, each element in the functions
array is a function that returns a promise.
By using map
, we can iterate over each function in functions
, and create a corresponding function in the new array that, when called, initiates a delay before calling the original function (fn
). After the delay, the original function should be called, and the promise it returns should be the result of the new function.
Here's how we arrive at the solution approach:
- Use
map
to iterate over each function in thefunctions
array. - For each function (
fn
), create a newasync
function that:- Waits for the specified
ms
milliseconds usingsetTimeout
wrapped in a promise. This is accomplished withawait new Promise(resolve => setTimeout(resolve, ms));
. Theawait
keyword pauses the function execution until the timeout completes. - Calls the original function
fn()
to get the promise it was supposed to return. - Returns the promise from calling
fn()
so that the returned value matches the intended asynchronous action of the original function.
- Waits for the specified
By defining the new function as async
, we are indicating that it is an asynchronous function that implicitly returns a promise, which can be awaited. This aligns with the requirement that each function in the new array should return a promise as well.
The solution effectively delays the resolution of each promise from the original array of functions, while maintaining the sequence and behavior they are intended to have.
Solution Approach
The implementation of the solution involves using a higher-order function, map
, and leveraging the async
/await
syntax of JavaScript to handle the timing of the promise resolution.
The map
function is a method available on arrays in JavaScript, which creates a new array with the results of calling a provided function on every element in the calling array. The provided function should take each element from the original array (in this case, functions
), apply a transformation, and return the new value that will be included in the new array.
In our case, we're using it to iterate over each function in the functions
array and create a new function that introduces a delay before execution. This is our transformation. The async
keyword is used to define the returning function as asynchronous, meaning it will return a Promise
and allow the use of await
inside its body.
Here is a step-by-step explanation of the code:
-
We call
map
on the originalfunctions
array. -
map
takes a function as its argument, which will be executed on each element (fn
) offunctions
. -
Inside this function, we return a new
async
function. This ensures that the returned function is an asynchronous function, which implicitly returns a promise. -
Within the new
async
function, the first operation isawait new Promise(resolve => setTimeout(resolve, ms));
. This creates a promise that resolves after a delay ofms
milliseconds.new Promise(resolve => setTimeout(resolve, ms))
creates a new promise that will execute thesetTimeout
function.setTimeout
is scheduled to callresolve()
afterms
milliseconds, resolving the promise.- By using
await
, we're telling JavaScript to wait until the promise is resolved (after the delay) before moving on to the next line.
-
Once the delay has finished, the original function
fn
is called. We returnfn()
which calls the original function and returns its promise. Since we're in anasync
function, the result offn()
is returned as a resolved value of the outer asynchronous function's promise. -
Each new function created by the
map
loop now includes the delay as described, and the resulting functions are collected into a new array. This new array is then returned by thedelayAll
function.
The result is an array of functions that match the behavior of the original functions but with each of their promises resolving after a delayed interval specified by ms
. This solution is elegant and cleanly utilizes JavaScript's handling of asynchronicity to enforce a simple yet crucial requirement—the delay between promise resolutions.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider an example to illustrate the solution approach with a simple scenario:
Suppose we have an array of functions functions
, where each function, when called, simply resolves a promise with its index in the array. We are given a delay ms
of 1000
milliseconds, which means we want to introduce a 1-second delay before each promise resolves.
// Original functions array
let functions = [
() => Promise.resolve('Function 1'),
() => Promise.resolve('Function 2'),
() => Promise.resolve('Function 3')
];
To apply the solution approach, we will follow these steps:
- We create a new array,
delayedFunctions
, by mapping over each function infunctions
.
// Apply transformation to introduce delay
let ms = 1000; // Delay of 1000 milliseconds
let delayedFunctions = functions.map((fn, index) => {
// Step 2: Return the new async function
return async () => {
// Step 4: Introduce delay
await new Promise(resolve => setTimeout(resolve, ms));
// Step 5: Call original function
return fn();
};
});
- Each element in
delayedFunctions
is now anasync
function that will incorporate the delay before calling the original function:
// Call each function in sequence with the delay
delayedFunctions[0]().then(console.log); // After 1 second, logs: "Function 1"
delayedFunctions[1]().then(console.log); // After 1 more second, logs: "Function 2"
delayedFunctions[2]().then(console.log); // After 1 more second, logs: "Function 3"
By calling the functions in sequence, we observe that each function waits for 1 second (the value of ms
) before resolving its promise. The output is logged to the console with a consistent 1-second delay between each message. This confirms that the solution approach works as intended, with each new function in delayedFunctions
behaving exactly like its counterpart in functions
, but with the additional specified delay before resolution.
Solution Implementation
1from typing import Callable, List
2import asyncio
3
4def delay_all(functions_array: List[Callable], delay_ms: int) -> List[Callable]:
5 """
6 Wraps each function in a given list with a delay.
7 The delayed functions, when invoked,
8 will wait for the specified milliseconds before executing the original function.
9
10 :param functions_array: The list of functions to be delayed.
11 :param delay_ms: The number of milliseconds to delay the function execution.
12 :return: A list of delayed functions.
13 """
14
15 async def delayed_function(original_function: Callable) -> Callable:
16 """
17 Returns an async function that delays the given original function.
18
19 :param original_function: The function to be delayed.
20 :return: The delayed function.
21 """
22 async def wrapper(*args, **kwargs):
23 # Delay the execution by sleeping for the specified amount of time
24 await asyncio.sleep(delay_ms / 1000) # asyncio.sleep uses seconds
25 # Invoke the original function after the delay
26 return original_function(*args, **kwargs)
27
28 return wrapper
29
30 # Map each original function to its delayed counterpart
31 return [delayed_function(func) for func in functions_array]
32
1import java.util.concurrent.*;
2import java.util.function.*;
3import java.util.List;
4import java.util.stream.Collectors;
5
6public class DelayFunctions {
7
8 /**
9 * Wraps each function in a given list with a delay.
10 * The delayed functions, when invoked, will wait for the specified milliseconds before executing the original function.
11 *
12 * @param functionsList The list of functions to be delayed.
13 * @param delayMs The number of milliseconds to delay the function execution.
14 * @return A list of delayed functions.
15 */
16 public static List<Supplier<Future<Object>>> delayAll(List<Supplier<Object>> functionsList, int delayMs) {
17 ExecutorService executor = Executors.newFixedThreadPool(functionsList.size());
18
19 // Map each original function to a new function that has a delay
20 return functionsList.stream().map(originalFunction -> (Supplier<Future<Object>>) () -> {
21 // Return a Future task
22 return executor.submit(() -> {
23 // Delay the execution by waiting for the specified amount of time
24 try {
25 TimeUnit.MILLISECONDS.sleep(delayMs);
26 } catch (InterruptedException e) {
27 Thread.currentThread().interrupt(); // Restore the interrupted status
28 // Handle the interrupted exception
29 }
30 // Invoke the original function after the delay
31 return originalFunction.get();
32 });
33 }).collect(Collectors.toList());
34 }
35}
36
1#include <functional>
2#include <vector>
3#include <chrono>
4#include <thread>
5
6/**
7 * Wraps each function in a given vector with a delay.
8 * The delayed functions, when invoked,
9 * will wait for the specified duration in milliseconds before executing
10 * the original function.
11 *
12 * @param functionsVector The vector of functions to be delayed.
13 * @param delayMs The number of milliseconds to delay the function execution.
14 * @return A vector of delayed functions.
15 */
16std::vector<std::function<void()>> delayAll(
17 const std::vector<std::function<void()>>& functionsVector,
18 int delayMs) {
19
20 // Create an empty vector of std::function objects to hold the delayed functions
21 std::vector<std::function<void()>> delayedFunctions;
22
23 // Iterate over the functions in the input vector
24 for (const auto& originalFunction : functionsVector) {
25 // Create a new function with a delay
26 std::function<void()> delayedFunction = [originalFunction, delayMs]() {
27 // Create a duration object for the delay
28 std::chrono::milliseconds duration(delayMs);
29 // Put the current thread to sleep for the time specified by duration
30 std::this_thread::sleep_for(duration);
31 // After the delay, call the original function
32 originalFunction();
33 };
34 // Add the delayed function to the vector of delayed functions
35 delayedFunctions.push_back(delayedFunction);
36 }
37
38 // Return the vector containing the delayed functions
39 return delayedFunctions;
40}
41
1/**
2 * Wraps each function in a given array with a delay.
3 * The delayed functions, when invoked,
4 * will wait for the specified milliseconds before executing the original function.
5 *
6 * @param {Function[]} functionsArray - The array of functions to be delayed.
7 * @param {number} delayMs - The number of milliseconds to delay the function execution.
8 * @returns {Function[]} An array of delayed functions.
9 */
10function delayAll(functionsArray: Function[], delayMs: number): Function[] {
11 return functionsArray.map(originalFunction => {
12 // Return a new async function
13 return async function delayedFunction(): Promise<any> {
14 // Delay the execution by waiting for the specified amount of time
15 await new Promise<void>(resolve => setTimeout(resolve, delayMs));
16 // Invoke the original function after the delay
17 return originalFunction();
18 };
19 });
20}
21
Time and Space Complexity
Time Complexity
The time complexity of the delayAll
function is O(n)
, where n
is the number of functions in the functions
array. The map function iterates over each function and wraps it with an asynchronous function that introduces a delay before invoking the original function. The delay itself (setTimeout(resolve, ms)
) does not add to the computational complexity, as it's merely setting up a timer and not performing any computation during the waiting time.
However, invoking each delayed function will incur the ms
delay plus the time complexity of the original function. If the time complexities of the provided functions are not uniform, the overall time complexity when executing all the delayed functions would be O(m + f(n))
, where m
is the constant delay (ms
) multiplied by n
and f(n)
represents the time complexity of the original functions which is executed after the delay.
Space Complexity
The space complexity of the delayAll
function is also O(n)
. This is due to the creation of a new array of asynchronous functions based on the length of the input functions
array. Each created function is essentially a closure that captures the fn
and ms
variables.
Beyond the array itself, the space required for each closure (the delayed asynchronous functions) does not depend on the size of the input array, but on the space complexity of the individual functions fn()
when they are called at runtime. Therefore, the overall space complexity of the entire operation when you include the runtime execution of the delayed functions would be O(n + s(n))
, where s(n)
is the additional space required by the execution of the original functions collectively.
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
Recommended Readings
LeetCode 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 we
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
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!