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?
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.
But why?!?
Easy there sport. Let's first get some terminology down.
A promise starts of as pending, it can either become:
fulfilled - marking it completed with a value.
rejected - marking it failed with a reason.
So far so good, but let's consider two additional terms:
resolved - meaning it resolved to another promise value and is tracking it.
settled - meaning it's actually fulfilled or rejected - either through the promise it's following resolving or on its own.
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".
Ok, you told me what happens - but why?!?!?!?
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 throwing 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.
What about rejecting with the unwrapped value?
That would mean we lose the information about which rejection we're dealing with. The only alternative is to reject with a Promise object.
Should I ever run into this?
No, never. You should never throw promises anyway and you should always reject with Errors.
Promise object construction:
new Promise( executor)
calls the executor function with two call back function arguments:
executor( resolve, reject)
where resolve is overloaded by argument type to either
link the promise to a thenable:
resolve( thenable); // link the resolved promise to the final state of the thenable.
where the resolved promise remains pending until the promise it is linked to ("was resolved with") becomes settled, or
fulfill the promise with something that is not a thenable
resolve (notThenable); // fulfill the promise with anything except a thenable.
Being javascript, "overloaded" in this context is performed by examination of the argument type and properties by resolve at run time, not when the script is compiled. A simplified explanation is that you can't fulfill a promise with a promise or promise like object.
The reject function is not overloaded and does not examine its argument at run time. If a promise is rejected with a promise or other thenable it is treated as any other rejection reason. The promise does not remain pending and becomes rejected. The promise used for rejection is passed as the reason argument to any functions catching promise rejection. A simplified explanation is that you can reject a promise with anything you like, but if it is not an Error object or descriptive reason you are on your own!
Promise.resolve() can take a value, a thenable or a promise. It adapts its behaviour.
Promise.reject() takes only an immediate value. So if you pass a Promise to it, it will clumsily try to treat it as an immediate value.
However, you do not consume the promise by passing it to Promise.reject. You can do this:
Promise.reject(myPromise); // weird and useless, and with no side effect
myPromise.then(e=>{console.log(e)}); // "consume" the promise
myPromise.then(e=>{console.log(e)}); // you can consume it as many times as you want
Related
I'm getting confused with the different terminology thrown around. From my understanding, a promise can be:
fulfilled
rejected
pending
settled
resolved
defer
Does resolve mean settled? or does it mean its fulfilled? and what the heck is defer?
Terminology can be hard.
Let's take from the Promises/A+ specification and the respective ES6 section that there are 3 states:
pending - the promise does not have taken a value yet, it's future is still uncertain.
fulfilled - the promise successfully got a result value "assigned"
rejected - the promise is given a reason why no result could be acquired, typically an error.
The term settled is a hyperonym for fulfilled and rejected, meaning either - the opposite of pending.
The dynamic verbs fulfill and reject describe changing the state from pending into either the fulfilled or rejected. These transitions are called fulfillment or rejection of the promise.
Those were easy. Now, resolve is a different beast. It sometimes is used synonymous to "fulfill", but it would better be understood as settling the promise's fate to either fulfilled or rejected. The resolution (seldom: settlement) of a promise means that it leaves the pending state. But not even that is accurate - the problem is the recursive nature of the Promise Resolution Procedure:
resolving a promise with a "plain" value means fulfilling it
resolving a promise with a promise (or thenable) means adopting its state:
resolving with a fulfilled promise is a fulfillment
resolving with a rejected promise is a rejection
resolving with a pending promise means waiting for its resolution
Yes, if a promise is resolved it might not even be known whether it's going to be fulfilled or rejected. But it means the fate is no longer undetermined, as it's bound to the promise that we resolved with (notice that you can resolve a promise only once).
Ignoring this special case, a resolved promise usually means a settled promise.
Or, to cite the ECMAScript 6 Specification:
A promise is resolved if it is settled or if it has been “locked in” to match the state of another promise. Attempting to resolve or reject a resolved promise has no effect. A promise is unresolved if it is not resolved. An unresolved promise is always in the pending state. A resolved promise may be pending, fulfilled or rejected.
and what the heck is defer?
Deferring a result means that you return an (asynchronous) promise for the result, and not the result directly (synchronously). And also return a deferred rejection instead of throwing synchronously.
Notice that "defer" is also used in some libraries (Q) as the method name to construct a Deferred object - see this answer on The differences between Deferred, Promise and Future for a good explanation.
Oh, and never trust variable names: defer might as well be an abbreviated "deferredObject".
The three promise states are listed in section 2.1 of the Promises/A+ specification.
From the specification:
So here are each of the terms you asked about:
Pending is the initial promise state. The operation represented by the promise has not yet been filfilled or rejected.
Fulfilled is another of the three promise states. It means that the promise has been resolved and now has its resolved value. The operation represented by the promise has been completed successfully.
Rejected is another of the three promise states. It means that the promise has been rejected and now has its rejected reason. The operation represented by the promise failed to obtain a value and thus has a reason for failing to do so (typically an error code or error object, but it can be anything).
Settled is a term that means the promise is either fulfilled or rejected (e.g. it's not pending any more), but it is not a separate state just a descriptive term to indicate it is no longer pending.
Resolved is a term that is often used to mean the same as fulfilled, but the two are not exactly the same. A promise can be resolved with a value which leads to fulfillment or it can be resolved with a rejected promise (which leads to rejection of this promise) or it can be resolved with a pending promise (which means it will now be waiting on the eventual state of some other promise).
It's hard to say exactly what you mean by defer. Promises are often classified as deferred objects in that they are an object that represents an action and result that is deferred to the future (it will occur in the future). In some promises implementations, there are actually two types of objects, a deferred object and a promise object. The deferred object is a superset of the promise object. Both can observe when the action is resolved or rejected with .then() handlers. But, only the deferred object can actually change the state to resolved or rejected.
In jQuery, you can create a deferred object with $.Deferred(). In other implementations such as ES6 promises, you just have promise objects with a constructor callback that has reject and resolve functions. The world is presumably moving toward what ES6 will have.
jQuery example that uses a deferred object:
function delay(t) {
var defer = $.Deferred();
setTimeout(function() {
defer.resolve();
}, t);
return defer.promise()
}
delay(200).then(function() {
// run my delayed function now
doMyThing();
});
ES6 promise example:
function delay(t) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, t);
});
}
delay(200).then(function() {
// run my delayed function now
doMyThing();
});
Domenic Denicola's "States and Fates" is a good, pithy summary.
States:
a promise is fulfilled if promise.then(f) will call f "as soon as possible"
a promise is rejected if promise.then(undefined, r) will call r "as soon as possible"
a promise is pending if it is neither fulfilled nor rejected.
Fates:
a promise is resolved if trying to resolve or reject it has no effect, i.e. the promise has been "locked in" to either follow another promise, or has been fulfilled or rejected
a promise is unresolved if it is not resolved, i.e. if trying to resolve or reject it will have an impact on the promise.
Follow the link for details 'relating states and fates'.
for the following promise chain
new Promise(r=>r('b').then(()=>Promise.reject('x'))).catch(err=>console.log({error}))
the inner promise is returned, so it should bubble up and catched by the outer catch block and logs the error to the console.
but instead, it got resolved.
Promise {<fulfilled>: 'b'}
how to catch the inner promise by an outer catch block?
r (more typically called resolve) does not return a promise, so calling .then on it is an error. Normally, if an error is thrown in a promise executor function (the function you pass new Promise), the promise being created is rejected with that error. But since you've already called resolve, you've fulfilled the promise with "b", and once fulfilled it cannot be changed to being rejected, so the error occurring during the executor is just suppressed. (This is a specific case of the more general rule that once a promise is resolved [tied to an outcome] it can't be resolved differently. A promise can be resolved without [yet] being fulfilled, but fulfilling it is one way of resolving it. If you're not 100% on this promise terminology, check out my blog post here explaining it.)
If you want to resolve the promise you're creating to another promise, you pass it into the resolve function:
new Promise(resolve => {
resolve(Promise.reject(new Error("x")));
})
.then(value => console.log("value", value))
.catch(error => console.error("error", error.message));
That specific example would be an example of the Explicit promise construction antipattern (we should just use the promise from Promise.reject directly), but if you had branching logic in the promise executor and some of the branches didn't involve promises, you might do something similar to this (though stylistically it would make more sense to me to throw an error or call the reject function passed to the executor).
In nearly all the documentation I read, promises are constructed with an executor function whose first argument is a function named "resolve":
var promise1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
But to me this is extremely confusing, because promise terminology differentiates "resolved" from "fulfilled," and this distinction is not consistently observed in the executor function naming.
According to https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md and https://promisesaplus.com/, "resolved" means that the promise has entered into some final state (fulfilled, rejected, or resolved to another promise), and "fulfilled" means the promise has been resolved successfully.
However, the executor's resolve() function is used to force the promise into a "fulfilled" state.
So my question is: why isn't the executor's resolve() function named fulfill()? Am I misunderstanding something?
Here's one possible explanation.
A promise ultimately ends up fulfilled or rejected and it can be send directly to one of those states by fulfilling with a value or rejecting with a reason.
But, a promise can also be tied to another thenable and it will then track that thenable. If that thenable ends up fulfilled, this promise will be fulfilled. If that other thenable ends up rejected, this promise will be rejected.
So, with the executor, you can actually do three things:
Fulfill to a value (that is not a thenable).
Reject to a reason
Resolve to a thenable which will be tracked and whose ultimate disposition will be inherited by this promise.
Ahhh, but you don't need three functions to implement all this. You can do it with just two. #1 and #3 can use the same function and just vary what they pass to it (either a thenable or a non-thenable value). So, you need a verb that captures both #1 and #3. Folks have generally picked resolve as that verb. It is resolved to either a value or thenable.
So, you get new Promise(function(resolve, reject) {...});. You have to pick some word for the first function. Most people writing documentation picked resolve(). If you resolve to a non-thenable value, you get a fulfilled promise. If you resolve to a thenable, this promise will track that thenable and take on it's eventual state.
I'm getting confused with the different terminology thrown around. From my understanding, a promise can be:
fulfilled
rejected
pending
settled
resolved
defer
Does resolve mean settled? or does it mean its fulfilled? and what the heck is defer?
Terminology can be hard.
Let's take from the Promises/A+ specification and the respective ES6 section that there are 3 states:
pending - the promise does not have taken a value yet, it's future is still uncertain.
fulfilled - the promise successfully got a result value "assigned"
rejected - the promise is given a reason why no result could be acquired, typically an error.
The term settled is a hyperonym for fulfilled and rejected, meaning either - the opposite of pending.
The dynamic verbs fulfill and reject describe changing the state from pending into either the fulfilled or rejected. These transitions are called fulfillment or rejection of the promise.
Those were easy. Now, resolve is a different beast. It sometimes is used synonymous to "fulfill", but it would better be understood as settling the promise's fate to either fulfilled or rejected. The resolution (seldom: settlement) of a promise means that it leaves the pending state. But not even that is accurate - the problem is the recursive nature of the Promise Resolution Procedure:
resolving a promise with a "plain" value means fulfilling it
resolving a promise with a promise (or thenable) means adopting its state:
resolving with a fulfilled promise is a fulfillment
resolving with a rejected promise is a rejection
resolving with a pending promise means waiting for its resolution
Yes, if a promise is resolved it might not even be known whether it's going to be fulfilled or rejected. But it means the fate is no longer undetermined, as it's bound to the promise that we resolved with (notice that you can resolve a promise only once).
Ignoring this special case, a resolved promise usually means a settled promise.
Or, to cite the ECMAScript 6 Specification:
A promise is resolved if it is settled or if it has been “locked in” to match the state of another promise. Attempting to resolve or reject a resolved promise has no effect. A promise is unresolved if it is not resolved. An unresolved promise is always in the pending state. A resolved promise may be pending, fulfilled or rejected.
and what the heck is defer?
Deferring a result means that you return an (asynchronous) promise for the result, and not the result directly (synchronously). And also return a deferred rejection instead of throwing synchronously.
Notice that "defer" is also used in some libraries (Q) as the method name to construct a Deferred object - see this answer on The differences between Deferred, Promise and Future for a good explanation.
Oh, and never trust variable names: defer might as well be an abbreviated "deferredObject".
The three promise states are listed in section 2.1 of the Promises/A+ specification.
From the specification:
So here are each of the terms you asked about:
Pending is the initial promise state. The operation represented by the promise has not yet been filfilled or rejected.
Fulfilled is another of the three promise states. It means that the promise has been resolved and now has its resolved value. The operation represented by the promise has been completed successfully.
Rejected is another of the three promise states. It means that the promise has been rejected and now has its rejected reason. The operation represented by the promise failed to obtain a value and thus has a reason for failing to do so (typically an error code or error object, but it can be anything).
Settled is a term that means the promise is either fulfilled or rejected (e.g. it's not pending any more), but it is not a separate state just a descriptive term to indicate it is no longer pending.
Resolved is a term that is often used to mean the same as fulfilled, but the two are not exactly the same. A promise can be resolved with a value which leads to fulfillment or it can be resolved with a rejected promise (which leads to rejection of this promise) or it can be resolved with a pending promise (which means it will now be waiting on the eventual state of some other promise).
It's hard to say exactly what you mean by defer. Promises are often classified as deferred objects in that they are an object that represents an action and result that is deferred to the future (it will occur in the future). In some promises implementations, there are actually two types of objects, a deferred object and a promise object. The deferred object is a superset of the promise object. Both can observe when the action is resolved or rejected with .then() handlers. But, only the deferred object can actually change the state to resolved or rejected.
In jQuery, you can create a deferred object with $.Deferred(). In other implementations such as ES6 promises, you just have promise objects with a constructor callback that has reject and resolve functions. The world is presumably moving toward what ES6 will have.
jQuery example that uses a deferred object:
function delay(t) {
var defer = $.Deferred();
setTimeout(function() {
defer.resolve();
}, t);
return defer.promise()
}
delay(200).then(function() {
// run my delayed function now
doMyThing();
});
ES6 promise example:
function delay(t) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, t);
});
}
delay(200).then(function() {
// run my delayed function now
doMyThing();
});
Domenic Denicola's "States and Fates" is a good, pithy summary.
States:
a promise is fulfilled if promise.then(f) will call f "as soon as possible"
a promise is rejected if promise.then(undefined, r) will call r "as soon as possible"
a promise is pending if it is neither fulfilled nor rejected.
Fates:
a promise is resolved if trying to resolve or reject it has no effect, i.e. the promise has been "locked in" to either follow another promise, or has been fulfilled or rejected
a promise is unresolved if it is not resolved, i.e. if trying to resolve or reject it will have an impact on the promise.
Follow the link for details 'relating states and fates'.
I read in docs.angularjs.org that on a deferred object, a promise is resolved with a value and here are my couple of questions.
var dfd = $q.deferred();
dfd.resolve() - what happens when resolving without a value? Is it a rejection?
resolve(value) – resolves the derived promise with the value. If the value is a rejection constructed via $q.reject, the promise will be rejected instead what does it mean?
Can anyone please clarify?
dfd.resolve() - what happens when resolving without a value? Is it a
rejection?
No, it's just the same as dfd.resolve(undefined) - you call a function with a parameter without arguments.
resolve(value) "If the value is a rejection constructed via
$q.reject, the promise will be rejected instead" - what does it mean?
If the value is a promise, that promise's state will be adopted. $q.reject does construct a rejected promise. It'll work with fulfilled promises as well, like resolve($q.when(value))