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.

LeetCode Problem 2723

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 number
  • promise2, 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

  1. Accept the two input promises, promise1 and promise2.
  2. Use Promise.all() to wait for both promises simultaneously. This ensures we do not unnecessarily serialize asynchronous work.
  3. Store the resolved values returned by Promise.all(). The result will be an array containing the values from promise1 and promise2.
  4. Add the two resolved numbers together.
  5. 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.