2723. Add Two Promises
Problem Description
This problem asks you to create a function that takes two promises as input, where each promise will resolve to a number. Your task is to return a new promise that resolves to the sum of the two numbers from the input promises.
The function signature is:
- Input: Two promises (
promise1
andpromise2
), each resolving to a number - Output: A single promise that resolves to the sum of the two numbers
For example, if promise1
resolves to 2
and promise2
resolves to 2
, the returned promise should resolve to 4
.
The solution uses async/await
syntax to handle the asynchronous operations. By using await
on each promise, the function waits for both promises to resolve and then adds their resolved values together. The async
function automatically wraps the result in a promise, which is exactly what the problem requires.
The key insight is that you need to wait for both promises to complete before you can add their values, and the result itself must be wrapped in a promise to match the expected return type Promise<number>
.
Intuition
When working with promises that resolve to values we want to combine, we need to think about the asynchronous nature of the operations. Since both promises will eventually produce numbers, and we want to add them, we need to wait for both values to be available before performing the addition.
The natural approach is to use async/await
because it allows us to write asynchronous code that looks and behaves like synchronous code. When we mark a function as async
, it automatically returns a promise, which satisfies our requirement of returning Promise<number>
.
Inside the function, we use await
on each promise to extract their resolved values. The expression (await promise1) + (await promise2)
first waits for promise1
to resolve and gets its value, then waits for promise2
to resolve and gets its value, and finally adds them together. The beauty of this approach is its simplicity - we're essentially telling JavaScript to "wait for both values, then add them."
The alternative would be to use .then()
chaining or Promise.all()
, but the async/await
approach is the most straightforward and readable. Since the function is marked as async
, the sum is automatically wrapped in a promise when returned, giving us exactly what we need without any extra promise construction.
Solution Approach
The implementation uses the async/await
pattern to handle the asynchronous operations cleanly:
async function addTwoPromises(
promise1: Promise<number>,
promise2: Promise<number>,
): Promise<number> {
return (await promise1) + (await promise2);
}
Let's break down how this works step by step:
-
Function Declaration: The function is declared with the
async
keyword, which means it will automatically return a promise. This satisfies our return type requirement ofPromise<number>
. -
Awaiting the First Promise: When we write
await promise1
, the execution pauses untilpromise1
resolves. Once it resolves, we get the actual number value (not the promise object). -
Awaiting the Second Promise: Similarly,
await promise2
pauses execution untilpromise2
resolves and gives us its number value. -
Addition Operation: Once both promises have resolved, we have two number values that can be added together using the standard
+
operator. -
Automatic Promise Wrapping: Since the function is
async
, the result of the addition is automatically wrapped in a promise before being returned.
The execution flow can be visualized as:
- Start with two Promise
objects - Extract the number from promise1 using
await
- Extract the number from promise2 using
await
- Add the two numbers together
- Return the sum wrapped in a Promise
This approach handles the promises sequentially (promise1 first, then promise2), but both promises can be executing concurrently in the background. The function simply waits for both to complete before performing the addition.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to understand how the solution works:
Example Input:
promise1 = new Promise(resolve => setTimeout(() => resolve(10), 50))
promise2 = new Promise(resolve => setTimeout(() => resolve(5), 30))
Step-by-step execution:
-
Function Call: We call
addTwoPromises(promise1, promise2)
-
Initial State:
- Both promises start executing immediately when created
promise1
will resolve to10
after 50mspromise2
will resolve to5
after 30ms
-
First Await (
await promise1
):- The function encounters
await promise1
and pauses - Even though
promise2
resolves first (at 30ms), we're waiting forpromise1
- After 50ms,
promise1
resolves to10
- The value
10
is extracted
- The function encounters
-
Second Await (
await promise2
):- The function encounters
await promise2
- Since
promise2
has already resolved (it finished at 30ms), we immediately get the value5
- No additional waiting is needed
- The function encounters
-
Addition:
- Now we have both values:
10
and5
- The addition is performed:
10 + 5 = 15
- Now we have both values:
-
Return:
- The
async
function automatically wraps15
in a promise - Returns
Promise<15>
which will resolve to15
- The
Timeline visualization:
Time: 0ms 30ms 50ms promise1: [-------|-------✓] resolves to 10 promise2: [-------✓] resolves to 5 Function: waits→ gets both values → returns Promise<15>
The key insight is that even though the promises resolve at different times, the function waits for both to complete before performing the addition, and the result is automatically wrapped in a promise.
Solution Implementation
1import asyncio
2from typing import Awaitable
3
4async def addTwoPromises(
5 promise1: Awaitable[int],
6 promise2: Awaitable[int]
7) -> int:
8 """
9 Adds the resolved values of two number promises together.
10
11 Args:
12 promise1: First promise that resolves to a number
13 promise2: Second promise that resolves to a number
14
15 Returns:
16 A promise that resolves to the sum of both resolved values
17 """
18 # Await both promises to resolve and return their sum
19 return (await promise1) + (await promise2)
20
21
22# Example usage:
23# async def main():
24# result = await addTwoPromises(
25# asyncio.create_task(asyncio.coroutine(lambda: 2)()),
26# asyncio.create_task(asyncio.coroutine(lambda: 2)())
27# )
28# print(result) # Output: 4
29#
30# asyncio.run(main())
31
1import java.util.concurrent.CompletableFuture;
2
3/**
4 * Adds the resolved values of two number promises together
5 * @param promise1 - First promise that resolves to an integer
6 * @param promise2 - Second promise that resolves to an integer
7 * @return A CompletableFuture that resolves to the sum of both resolved values
8 */
9public class PromiseAdder {
10
11 public static CompletableFuture<Integer> addTwoPromises(
12 CompletableFuture<Integer> promise1,
13 CompletableFuture<Integer> promise2) {
14
15 // Combine both futures and apply the sum operation when both complete
16 return promise1.thenCombine(promise2, (num1, num2) -> num1 + num2);
17 }
18
19 /**
20 * Example usage:
21 * public static void main(String[] args) {
22 * CompletableFuture<Integer> result = addTwoPromises(
23 * CompletableFuture.completedFuture(2),
24 * CompletableFuture.completedFuture(2)
25 * );
26 *
27 * // Print the result when available
28 * result.thenAccept(System.out::println); // Output: 4
29 * }
30 */
31}
32
1#include <future>
2#include <iostream>
3
4/**
5 * Adds the resolved values of two number promises (futures) together
6 * @param promise1 - First future that resolves to an integer
7 * @param promise2 - Second future that resolves to an integer
8 * @returns A future that resolves to the sum of both resolved values
9 */
10std::future<int> addTwoPromises(
11 std::future<int> promise1,
12 std::future<int> promise2) {
13 // Create an async task that waits for both futures and returns their sum
14 return std::async(std::launch::async, [](std::future<int> p1, std::future<int> p2) {
15 // Get the values from both futures (blocking until they're ready)
16 int value1 = p1.get();
17 int value2 = p2.get();
18
19 // Return the sum of both values
20 return value1 + value2;
21 }, std::move(promise1), std::move(promise2));
22}
23
24/**
25 * Example usage:
26 * auto promise1 = std::async(std::launch::async, []() { return 2; });
27 * auto promise2 = std::async(std::launch::async, []() { return 2; });
28 * auto result = addTwoPromises(std::move(promise1), std::move(promise2));
29 * std::cout << result.get() << std::endl; // Output: 4
30 */
31
1/**
2 * Adds the resolved values of two number promises together
3 * @param promise1 - First promise that resolves to a number
4 * @param promise2 - Second promise that resolves to a number
5 * @returns A promise that resolves to the sum of both resolved values
6 */
7async function addTwoPromises(
8 promise1: Promise<number>,
9 promise2: Promise<number>,
10): Promise<number> {
11 // Await both promises to resolve and return their sum
12 return (await promise1) + (await promise2);
13}
14
15/**
16 * Example usage:
17 * addTwoPromises(Promise.resolve(2), Promise.resolve(2))
18 * .then(console.log); // Output: 4
19 */
20
Time and Space Complexity
Time Complexity: O(max(T1, T2))
where T1
and T2
are the resolution times of promise1
and promise2
respectively.
The function uses await
on both promises sequentially. While the syntax appears sequential, JavaScript's event loop and the nature of promises mean that:
- Both promises start executing when they're created (before being passed to the function)
- The
await
operators simply wait for their resolution - The actual time taken is determined by whichever promise takes longer to resolve
- The addition operation itself is
O(1)
Space Complexity: O(1)
The function only stores:
- Two resolved numeric values from the promises
- The sum of these values
- No additional data structures or recursive calls are made
- The space used is constant regardless of the input values
Common Pitfalls
1. Sequential Waiting Instead of Parallel Execution
The current implementation waits for promises sequentially, which can be inefficient:
# Inefficient: waits for promise1 to complete before starting to wait for promise2 return (await promise1) + (await promise2)
If promise1
takes 2 seconds and promise2
takes 3 seconds, the total execution time will be approximately 3 seconds if they run in parallel, but could be up to 5 seconds if truly sequential.
Solution: Use asyncio.gather()
to ensure parallel execution:
async def addTwoPromises(
promise1: Awaitable[int],
promise2: Awaitable[int]
) -> int:
# Run both promises in parallel
result1, result2 = await asyncio.gather(promise1, promise2)
return result1 + result2
2. Missing Error Handling
If either promise rejects/raises an exception, the function will propagate the error without any handling:
# If promise1 or promise2 raises an exception, it bubbles up uncaught return (await promise1) + (await promise2)
Solution: Add try-except blocks or use default values:
async def addTwoPromises(
promise1: Awaitable[int],
promise2: Awaitable[int]
) -> int:
try:
result1, result2 = await asyncio.gather(
promise1,
promise2,
return_exceptions=False # Raises first exception encountered
)
return result1 + result2
except Exception as e:
# Handle error appropriately
raise ValueError(f"Failed to add promises: {e}")
3. Type Confusion with Coroutines vs Tasks
In Python, there's a distinction between coroutines, tasks, and futures. Passing raw coroutines without properly creating tasks can lead to unexpected behavior:
# Problematic: passing raw coroutines
async def get_number():
return 2
# Wrong way - coroutine objects aren't automatically scheduled
result = await addTwoPromises(get_number(), get_number())
Solution: Ensure proper task creation:
# Correct way - create tasks explicitly task1 = asyncio.create_task(get_number()) task2 = asyncio.create_task(get_number()) result = await addTwoPromises(task1, task2)
4. Resource Cleanup on Cancellation
If the parent coroutine is cancelled while waiting, both promises might continue running in the background:
Solution: Use proper cancellation handling:
async def addTwoPromises(
promise1: Awaitable[int],
promise2: Awaitable[int]
) -> int:
try:
return (await promise1) + (await promise2)
except asyncio.CancelledError:
# Ensure both promises are cancelled if one fails
if hasattr(promise1, 'cancel'):
promise1.cancel()
if hasattr(promise2, 'cancel'):
promise2.cancel()
raise
What's the relationship between a tree and a graph?
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!