If a Promise p
is resolved with the value of a Promise (or Thenable) q
, it essentially becomes a copy of Promise q
. If q
is resolved, p
will be resolved with the same value.
Promise.resolve(Promise.resolve("hello"));
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "hello"}
If q
is rejected, p
will be rejected with the same value.
Promise.resolve(Promise.reject(new Error("goodbye")));
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: goodbye}
The fact that Promise p
was resolved/rejected through Promise q
, instead of directly with the respective value, is irrelevant to the final result. The intermediate Promise is consumed as part of the resolution process, and is not visible to the consumer.
If q
is a never resolved or rejected, p
will also remain pending forever.
Promise.resolve(new Promise(() => null)); // perpetually-pending promise
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
These cases are well-known, but I have never seen what happens if a Promise is rejected (instead of resolved) with another Promise value. Does the rejection process also consume intermediate Promises, or are they passed through intact?
If it does consume them, how does that work?
2) Rejected promises example The Promise. all() returns a Promise that is rejected if any of the input promises are rejected. In this example, we have three promises: the first one is resolved after 1 second, the second is rejected after 2 seconds, and the third one is resolved after 3 seconds.
The Promise. reject() method is used to return a rejected Promise object with a given reason for rejection. It is used for debugging purposes and selective error catching.
Show activity on this post. Since promises can only resolve once (to either fulfilled or rejected), the first resolution wins and any further calls will be ignored. From the docs: In all cases where a promise is resolved (i.e. either fulfilled or rejected), the resolution is permanent and cannot be reset.
The Promise. reject() method returns a Promise object that is rejected with a given reason.
Let's see what happens if we reject a Promise p
with a resolved Promise q
:
Promise.reject(Promise.resolve("hello"));
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Promise}
Uncaught (in promise) Promise {
[[PromiseStatus]]: "resolved", [[PromiseValue]]: "hello"}
Or more explicitly:
const q = Promise.resolve("hello");
const p = Promise.reject(q);
p.then(null, x => console.log("Rejection value:", x));
Rejection value: Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "hello"}
The Promise q
, the rejection value, is never unwrapped! p
's rejection handlers are called with the the Promise q
itself, not the value it contains.
This also means that p
's rejection handler doesn't need to wait for q
to be resolved before it can run. Even if q
is never resolved, p
's rejection handler can still be called.
Promise.reject(new Promise(() => null)); // Reject with perpetually-pending Promise
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Promise}
Uncaught (in promise) Promise {
[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
Finally, let's confirm the behaviour if we reject Promise p
using another a rejected Promise q
:
Promise.reject(Promise.reject(new Error("goodbye")));
Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Promise}
Uncaught (in promise) Error: goodbye(…)(anonymous function)
Uncaught (in promise) Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: goodbye}
We see again that q
is not unwrapped, and p
's rejection handler will be called with q
itself, not the value that q
has been rejected with.
So, Jeremy's answer explains what happens:
const p = Promise.reject(Promise.resolve(3));
p
is a rejected promise with the rejection value of a Promise
of 3.
We were taught to believe promises never resolve with promises! Well, this is a special case. Here, we are rejecting a promise with another promise in contradiction to what then
does.
Easy there sport. Let's first get some terminology down.
A promise starts of as pending, it can either become:
So far so good, but let's consider two additional terms:
Phew. Now that that's out of the way:
What Promise.resolve
does is create a promise resolved to another value. If that value is a promise it tracks it - otherwise it settles immediately with the value passed in. This is also what happens if you return
from within a then
or await
something in an async
function.
What Promise.reject
does is create a promise rejected with another value. It has no chance to follow another promise as it is immediately created with a rejected result.
This behavior is specified in reject
and resolve
. In particular - we're creating a promise capability and resolve
is special - namely look at "Promise Resolve Functions".
Well, let's consider the alternatives. We want resolve
to mimic returning from a then
or awaiting in an async
function and reject
to mimic throw
ing in a then
or in an async
function.
const p = Promise.resolve().then(() => {
throw Promise.reject(5);
});
It is clearer to see resolving p
to 5 makes no sense! We'd mark the promise as completed correctly but it clearly did not complete correctly.
Similarly:
async function foo() {
throw Promise.resolve(5);
}
foo(); // no one would expect foo
to resolve.
That would mean we lose the information about which rejection we're dealing with. The only alternative is to reject with a Promise
object.
No, never. You should never throw
promises anyway and you should always reject with Error
s.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With