LeetCode 2723 - Add Two Promises
This problem asks us to work with JavaScript promises and asynchronous execution. We are given two promises, promise1 and promise2, and each promise is guaranteed to eventually resolve to a numeric value.
Difficulty: 🟢 Easy
Topics: —
Solution
Problem Understanding
This problem asks us to work with JavaScript promises and asynchronous execution. We are given two promises, promise1 and promise2, and each promise is guaranteed to eventually resolve to a numeric value. Our task is to return a new promise that resolves to the sum of the two resolved values.
In simpler terms, imagine two asynchronous operations are happening independently. One might finish after 20 milliseconds and another after 60 milliseconds. We do not care about how long each takes or in what order they complete. We only care about obtaining both final values and returning their arithmetic sum.
The input consists of two promises:
promise1, which resolves to a numberpromise2, which resolves to a number
The expected output is another promise. When both input promises finish resolving, the returned promise should itself resolve to the numerical sum of their resolved values.
A key detail is that the problem guarantees both promises resolve successfully with numbers. This means we do not need to handle rejection cases or invalid inputs. Since the constraints are extremely small and no upper bound on execution time is judged, efficiency is not a major concern here. The challenge is primarily about understanding how to coordinate asynchronous operations cleanly.
There are a few edge cases worth considering, even though the problem guarantees valid input. The resolved values may be negative, zero, or very large numbers. The promises may resolve in different orders or at significantly different times. A naive implementation that assumes one promise finishes before the other could fail, but JavaScript promise utilities already provide mechanisms to synchronize asynchronous work safely.
Approaches
Brute Force Approach
A straightforward approach is to wait for the first promise to resolve, then wait for the second promise to resolve, and finally add the values together.
This can be done using sequential await operations. For example, we could first await promise1, store its result, then await promise2, and compute the sum.
This approach works correctly because it guarantees we eventually retrieve both resolved numbers before performing the addition. However, it is inefficient from an asynchronous programming perspective because the waits happen sequentially. Even though both promises are already running concurrently, our code unnecessarily waits for one before checking the other.
For example, if promise1 takes 100 ms and promise2 takes 200 ms, sequential waiting effectively delays access to the second result even though it may already be resolving independently.
Optimal Approach
The key insight is that we want to wait for both promises simultaneously rather than sequentially. Since both promises are independent, there is no dependency between them.
JavaScript provides Promise.all() specifically for this purpose. It takes an array of promises and returns a new promise that resolves when all input promises resolve. The resolved values are returned in an array, preserving order.
Using Promise.all([promise1, promise2]), we can retrieve both values at once and immediately compute their sum.
This is the cleanest and most idiomatic solution because it explicitly models the problem requirement: wait for both asynchronous computations and combine their results.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(1) | O(1) | Sequentially waits for both promises using separate awaits |
| Optimal | O(1) | O(1) | Uses Promise.all() to resolve both concurrently |
Although the actual runtime depends on how long the promises take to resolve, algorithmically there are only two fixed operations, so complexity remains constant.
Algorithm Walkthrough
- Accept the two input promises,
promise1andpromise2. - Use
Promise.all()to wait for both promises simultaneously. This ensures we do not unnecessarily serialize asynchronous work. - Store the resolved values returned by
Promise.all(). The result will be an array containing the values frompromise1andpromise2. - Add the two resolved numbers together.
- Return the resulting sum as the resolved value of a new promise.
Why it works
The correctness comes from the behavior of Promise.all(). It guarantees that execution proceeds only after every input promise has resolved. Since the problem guarantees both promises resolve to numbers, we know we will receive exactly two valid numeric values. Adding these values produces the required answer, and the returned promise resolves with the correct sum.
Python Solution
Since this is a JavaScript promise problem, LeetCode expects a JavaScript implementation rather than Python. Below is the correct LeetCode-submittable solution.
# Python is not applicable for this JavaScript-only problem.
# The actual LeetCode submission requires JavaScript.
#
# Equivalent conceptual implementation:
import asyncio
async def add_two_promises(promise1, promise2):
value1, value2 = await asyncio.gather(promise1, promise2)
return value1 + value2
The Python version above demonstrates the same asynchronous idea using asyncio.gather(), which behaves similarly to JavaScript's Promise.all(). Both asynchronous tasks are awaited concurrently, and once both results are available, their sum is returned.
The important concept is concurrency. Rather than waiting for one asynchronous operation to finish before starting another, we allow both to progress independently and synchronize only when their results are needed.
Go Solution
Since this problem is designed specifically around JavaScript promises, there is no official Go stub on LeetCode. However, the equivalent concurrency idea in Go can be implemented using goroutines and channels.
package main
func addTwoPromises(ch1 <-chan int, ch2 <-chan int) int {
value1 := <-ch1
value2 := <-ch2
return value1 + value2
}
The Go implementation models promises using channels. Each channel asynchronously provides an integer value. Reading from a channel blocks until a value becomes available, similar to awaiting a promise. Once both values are received, we add them together and return the result.
Unlike JavaScript promises, Go uses goroutines and channels for concurrency. There is no direct equivalent to Promise.all(), but channels naturally synchronize asynchronous computation.
Worked Examples
Example 1
Input
promise1 = resolves to 2 after 20 ms
promise2 = resolves to 5 after 60 ms
Step-by-step execution:
| Step | Event | State |
|---|---|---|
| 1 | Promise.all() starts waiting |
Waiting for both promises |
| 2 | promise1 resolves |
[2, pending] |
| 3 | promise2 resolves |
[2, 5] |
| 4 | Sum computed | 2 + 5 = 7 |
| 5 | Returned promise resolves | 7 |
Final output:
7
Example 2
Input
promise1 = resolves to 10 after 50 ms
promise2 = resolves to -12 after 30 ms
Step-by-step execution:
| Step | Event | State |
|---|---|---|
| 1 | Promise.all() starts waiting |
Waiting for both promises |
| 2 | promise2 resolves first |
[pending, -12] |
| 3 | promise1 resolves |
[10, -12] |
| 4 | Sum computed | 10 + (-12) = -2 |
| 5 | Returned promise resolves | -2 |
Final output:
-2
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(1) | Only two promises are processed, regardless of their resolution times |
| Space | O(1) | Only a fixed amount of additional memory is used |
The actual wall-clock execution time depends on when the promises resolve, but from an algorithmic perspective, the work performed is constant. We only wait for two fixed asynchronous values and perform one addition.
Test Cases
import asyncio
async def add_two_promises(promise1, promise2):
value1, value2 = await asyncio.gather(promise1, promise2)
return value1 + value2
async def resolved(value):
return value
async def run_tests():
# Example 1
assert await add_two_promises(
resolved(2),
resolved(5)
) == 7 # basic positive numbers
# Example 2
assert await add_two_promises(
resolved(10),
resolved(-12)
) == -2 # negative number handling
# Zero values
assert await add_two_promises(
resolved(0),
resolved(0)
) == 0 # both values are zero
# Mixed positive and negative
assert await add_two_promises(
resolved(-5),
resolved(8)
) == 3 # opposite signs
# Large numbers
assert await add_two_promises(
resolved(1_000_000),
resolved(2_000_000)
) == 3_000_000 # large value support
# Both negative
assert await add_two_promises(
resolved(-10),
resolved(-15)
) == -25 # negative sum
asyncio.run(run_tests())
| Test | Why |
|---|---|
(2, 5) |
Validates the first example |
(10, -12) |
Validates negative number handling |
(0, 0) |
Ensures zero values work correctly |
(-5, 8) |
Tests mixed sign addition |
(1_000_000, 2_000_000) |
Confirms large values behave correctly |
(-10, -15) |
Ensures negative sums are handled |
Edge Cases
Promises Resolve in Different Orders
One promise may resolve much faster than the other. For example, promise1 could resolve instantly while promise2 takes several seconds. A buggy implementation might incorrectly assume both complete at the same time. Promise.all() correctly waits for every promise before continuing, so ordering does not matter.
Negative Numbers
The resolved values can be negative. A careless implementation might accidentally assume positive values only or mishandle subtraction logic. Since we directly add the resolved values using standard arithmetic, negative numbers work naturally.
Zero Values
Both promises may resolve to 0. Some implementations accidentally treat 0 as a falsy value and introduce logic errors. Since our solution performs direct numerical addition without conditional checks, 0 + 0 is handled correctly and returns 0.