---
title: "What Will Happen if We Resolve a Promise Instance With Another Promise Instance?"
description: "Explore how JavaScript adopts the state of another Promise when resolve receives a Promise, including pending, fulfilled, and rejected cases."
lang: "en"
tags: ["JavaScript", "Promises", "async-await"]
pubDate: 2022-01-04T00:00:00.000Z
updatedDate: 2024-02-03T00:00:00.000Z
---

We all know how to resolve an instance of Promise:

```js
const promise = new Promise((resolve) => {
  const someValue = "some value";
  setTimeout(() => {
    resolve(someValue);
  }, 1000);
});
```

After 1 second, the `promise` will be resolved with `someValue`.

But when we resolve a Promise instance with another Promise instance, what will happen?

## Resolve with a Pending Promise Instance

First, let's see what will happen when we resolve a Promise instance with another Promise instance that is pending.

<!-- prettier-ignore-start -->
```js annotated
const button = document.createElement("button");
button.appendChild(document.createTextNode("test 1 (pending)"));
document.body.appendChild(button);

function someAsyncTask() {
  return new Promise(
    (resolveInner) => {
      setTimeout(
        () => {
          console.log("the inner resolve start");
          resolveInner();
          console.log("the inner resolve end");
        },
        1000
      );
    }
  )
}

button.addEventListener("click", () => {
  const outerPromise = new Promise(
    (resolveOuter) => {
      const innerPromise = someAsyncTask();

      innerPromise.then(() => {
        console.log("the inner's then callback");
      });

      console.log("the outer resolve start");
      resolveOuter(innerPromise);
      console.log("the outer resolve end");
    }
  );

  outerPromise.then(() => {
    console.log("the outer's then callback");
  });
});
```
<!-- prettier-ignore-end -->

What's the output to the console?

```text
"the outer try to resolve"
"the outer resolved"

(after about 1 second)

"the inner try to resolve"
"inner resolved"
"the inner's then callback"
"the outer's then callback"
```

We can see, the outer's `then` callback is called after the `pendingPromise` given to `resolveOuter`.

This means, after the `resolveOuter` function is called, the `outerPromise` is still pending.

And then, only after the `innerPromise` is resolved/fulfilled, the `outerPromise` is resolved/fulfilled.

You may also notice, the `innerPromise`'s `then` callback is called before the `outerPromise`'s `then` callback, indicating that the inner one is scheduled into the microtask queue before the outer one.

<!-- prettier-ignore-start -->
```js annotated
const button = document.createElement("button");
button.appendChild(document.createTextNode("test 1 (pending)"));
document.body.appendChild(button);

function someAsyncTask() {
  return new Promise(
    (resolveInner) => {
      setTimeout(
        () => {
    //  -------
    //     ^
    //  [This function is scheduled when setTimeout is called.]
    //  [After 1 sec, it will be push to macrotask queue.]
          console.log("the inner resolve start");
          //  ^
          // [3]
          resolveInner();
          console.log("the inner resolve end");
          //  ^
          // [4]
        },
        1000
      );
    }
  )
}

button.addEventListener("click", () => {
  const outerPromise = new Promise(
    (resolveOuter) => {
      const innerPromise = someAsyncTask();
        .then(() => {
          //  -------
          //     ^
          // [This function will be pushed to microtask queue when resolveInner()]
          console.log("the inner's then callback");
          //    ^
          //   [5]
        });

      console.log("the outer resolve start");
      //    ^
      //   [1]
      resolveOuter(innerPromise);
      console.log("the outer resolve end");
      //    ^
      //   [2]
    }
  );

  outerPromise.then(() => {
                //  -------
                //     ^
                // [This function will be pushed to microtask queue when resolveInner()]
                // [But after the callback of innerPromise.then()]
    console.log("the outer's then callback");
    //    ^
    //   [6]
  });
});
```
<!-- prettier-ignore-end -->

## Resolve with a Resolved Promise

Then, let's see what will happen when we resolve a Promise instance with another Promise instance that is resolved.

<!-- prettier-ignore-start -->
```js annotated
const button = document.createElement("button");
button.appendChild(document.createTextNode("test 2 (resolved)"));
document.body.appendChild(button);

button.addEventListener("click", () => {
  const outerPromise = new Promise(
    (resolveOuter) => {
      // @highlight
      const resolvedPromise = Promise.resolve();

      resolvedPromise.then(() => {
        console.log("the inner's then callback");
      });

      console.log("the outer try to resolve");
      resolveOuter(resolvedPromise);
      console.log("the outer resolve end");
    }
  );

  outerPromise.then(() => {
    console.log("the outer's then callback");
  });
});
```
<!-- prettier-ignore-end -->

What's the output to the console?

```text
"the outer try to resolve"
"the outer resolved"
"the inner's then callback"
"the outer's then callback"
```

Since the inner Promise instance is already resolved, the behavior here is the same as when we resolve with a non-Promise value.

## Resolve with a Rejected Promise

<!-- prettier-ignore-start -->
```js annotated
const button = document.createElement("button");
button.appendChild(document.createTextNode("test 3 (rejected)"));
document.body.appendChild(button);

button.addEventListener("click", () => {
  const outerPromise = new Promise(
    (resolveOuter) => {
      // @highlight
      const rejectedPromise = Promise.reject();

      rejectedPromise.then(() => {
        console.log("the inner's then callback");
      });

      console.log("the outer resolve start");
      resolveOuter(rejectedPromise);
      console.log("the outer resolved?");
    }
  );

  outerPromise.then(() => {
    console.log("the outer's then callback");
  });
});
```
<!-- prettier-ignore-end -->

What's the output to the console?

```text
"the outer try to resolve"
"the outer resolved?"
```

The resolve function is called, but the Promise instance is not resolved.

Instead, the outer Promise instance is actually rejected. We can use `catch` method to examine it:

```js
outerPromise.catch(() => {
  console.log("the outer is rejected");
});
```

## Conclusion

When we resolve a Promise instance with another Promise instance, the outer Promise instance's state will be determined by the inner Promise instance's state.

- If the inner Promise instance is pending, the outer Promise instance will be pending.
- If the inner Promise instance is resolved, the outer Promise instance will be resolved.
- If the inner Promise instance is rejected, the outer Promise instance will be rejected.

And the `then` callback of the inner Promise instance will be called before the `then` callback of the outer Promise instance.

However, this behavior is not common in real-world scenarios. Because, in most cases, we will use `async`/`await` to handle the Promise instance.

For example, the first pending example can be almost fully rewritten as:

<!-- prettier-ignore-start -->
```js annotated
// @dim start
const button = document.createElement("button");
button.appendChild(document.createTextNode("test 4 (async/await)"));
document.body.appendChild(button);

function someAsyncTask() {
  return new Promise(
    (resolveInner) => {
      setTimeout(
        () => {
          console.log("the inner resolve start");
          resolveInner();
          console.log("the inner resolve end");
        },
        1000
      );
    }
  )
}

button.addEventListener("click", () => {
  // @dim end
  const ourterPromise = async () => {
    await someAsyncTask().then(() => {
      console.log("the inner's then callback");
    });
    console.log("the outer's then callback");
  };
  
  outerPromise();
// @dim
});
```
<!-- prettier-ignore-end -->

The main difference here is that we don't have the opportunity to time the execution of statements such as `console.log("the outer resolve start");` or `console.log("the outer resolve end")` in the first example. There's no way to run the statements between when `someAsyncTask` is called and when it's returned promise is fulfilled.

However, the `async`/`await` method is more readable because its behavior is more intuitive.
