I understand Promises to exist in one of three states: A Promise can either be pending (unresolved), fulfilled (resolved successfully) or <
Indeed, the resolve callback does not imply that the promise will be fulfilled.
The terms fulfilled, rejected, pending, settled, resolved and locked-in are defined in the EcmaScript2015 specs, 25.4 Promise Objects:
Any Promise object is in one of three mutually exclusive states: fulfilled, rejected, and pending:
A promise
p
is fulfilled ifp.then(f, r)
will immediately enqueue a Job to call the functionf
.A promise
p
is rejected ifp.then(f, r)
will immediately enqueue a Job to call the functionr
.A promise is pending if it is neither fulfilled nor rejected.
A promise is said to be settled if it is not pending, i.e. if it is either fulfilled or rejected.
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.
A short overview, where I will use the term "autonomous" as the opposite of "locked in". They are the two possible values for a promise's dependency situation:
+---------------------+------------+-----------+-----------+----------+
| action | dependency | state | resolved? | settled? |
+---------------------+------------+-----------+-----------+----------+
| new Promise() | autonomous | pending | no | no |
| - resolve(thenable) | locked-in | pending* | yes | no |
| - resolve(other) | autonomous | fulfilled | yes | yes |
| - reject(any) | autonomous | rejected | yes | yes |
+---------------------+------------+-----------+-----------+----------+
* The thenable is now in control over the future state of our promise object.
The above quote mentions that a promise is locked-in to match the state "of another promise", but more precisely that "other promise" could also be a non-promise "thenable" as can be seen in the steps 11 and 12 of the process description in 25.4.1.3.2
- If IsCallable(thenAction) is
false
, then
a. Return FulfillPromise(promise, resolution).- Perform EnqueueJob (
"PromiseJobs"
, PromiseResolveThenableJob, «promise, resolution, thenAction»)
A demo of resolve
being called with a thenable, which in turn triggers a rejection:
const thenable = { // Could be a promise object, but does not have to be
then(success, fail) {
setTimeout(() => fail("gotcha!"), 1000);
}
}
const p = new Promise((resolve, reject) => {
console.log("1. The promise is created as pending");
setTimeout(() => {
resolve(thenable);
console.log("2. It's resolved with a thenable; it's not yet settled");
}, 1000);
});
p.catch(err =>
console.log(`3. It's settled as rejected with error message "${err}"`)
);