19

I expected the promise handler to log the promise p1 (not the value "A") since console.log is called with p1 directly. However, it somehow logs "A". How is the promise p1 automatically resolved to "A" without then being called on it ? For example, console.log(p1) does not output "A" directly as is expected. Is something going on behind the scenes ?

var p1 = new Promise(function(resolve, reject) {
  resolve("A");
});

var p2 = new Promise(function(resolve, reject) {
  resolve(p1);
});

p2.then(function(v) {
  console.log(v)
});

EDIT: I understand that calling

p1.then((v) => return v))

returns a new promise that is fulfilled with the value v. Unless Im seriously missing something here, the "p1" reference in the second promise constructor should have been passed directly to console.log, resulting in the block

var p2 = new Promise(function(resolve, reject) {
  resolve(p1);
});

p2.then(function(v) {
 console.log(v)
});

becoming

console.log(p1).

Since console.log is called directly with p1, NOT the result of p1.then(...), p1 should not be resolved into the value "A" in the same way that printing that a another program

var promise = new Promise(function(resolve, reject) {
  resolve("B")
})

console.log(promise)

does not result in the string "B".

EDIT2: I had a misconception that the resolve parameter passed to the executor is a wrapper for unfulfilled function, which caused me tons of confusion. Check out Why does the Promise constructor require a function that calls 'resolve' when complete, but 'then' does not - it returns a value instead? for more details.

kuan
  • 415
  • 1
  • 9
  • 16
  • 2
    That's the [difference between `resolve`ing and *fulfill*ing](https://stackoverflow.com/a/29269515/1048572). Yes, `then` *is* called behind the scenes: `resolve(p1)` becomes `p1.then(resolve, reject)` – Bergi Sep 29 '17 at 14:20
  • @Bergi I believe that your comment here is the answer I am looking for. More explanation as to how resolve(p1) becomes p1.then(resolve, reject) in an answer would be really helpful! – kuan Sep 29 '17 at 14:45
  • If you want to move a promise object through the `resolve` function down to the `.then()` stage you may consider nicely packing it by an array (along with some other thingies if needed) and access it further down like `.then(v => console.log(v[0]))`. Just a side note. – Redu Sep 29 '17 at 15:33
  • Please note that your original expectation is reasonable, because `resolve`'s semantics is "take a value and resolve it to a `Promise`". This value can be another `Promise`, of course and expecting that you get a nested `Promise` is plausible. However, the language designers decided to collapse both promises for short-term convenience. Collapsing two promises should be reserved for `then`, though. –  Mar 14 '18 at 13:38
  • Relevant bits of the specification: [The Promise Constructor](//tc39.es/ecma262/#sec-promise-executor) and [Promise Resolve Functions](//tc39.es/ecma262/#sec-promise-resolve-functions). `resolve(promise)` is equivalent to `.then(promise)` and both make use of [Promise Jobs](//tc39.es/ecma262/#sec-promise-jobs) and [Host Operations to Enqueue Jobs](//tc39.es/ecma262/#sec-jobs), a.k.a. the “job queue”. – Sebastian Simon Nov 29 '21 at 00:57

3 Answers3

14

From the MDN documentation:

Promise.resolve(value)

Returns a Promise object that is resolved with the given value. If the value is a thenable (i.e. has a then method), the returned promise will "follow" that thenable, adopting its eventual state; otherwise the returned promise will be fulfilled with the value. Generally, if you don't know if a value is a promise or not, Promise.resolve(value) it instead and work with the return value as a promise.

p1 is thenable, so the return promise follows it.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • 6
    `Promise.resolve` is not called in the OPs code, though – Bergi Sep 29 '17 at 14:21
  • 1
    @Bergi OP is essentially doing like `Promise.resolve(Promise.resolve("A")).then(v => console.log(v));` It would be quite awkward for `new Promise((v,x) => v(new Promise((v,x) => v("A")))).then(v => console.log(v))` to conflict this explanation. – Redu Sep 29 '17 at 15:25
  • 3
    @Redu Yes, "essentially". However the OP probably doesn't know this, and it wasn't explicitly stated in the answer. – Bergi Sep 29 '17 at 17:22
12

Resolving a promise to another promise will automatically make it wait for the other promise's result.

This is what makes promises chainable (returning further promises in then() callbacks).

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Are you talking about returning a promise in the then function handler? I was aware of that but clearly the Promise.prototype.then call did not return a promise. thanks. – kuan Sep 29 '17 at 14:22
  • @kuan Yes, that's [the magic of promises](https://stackoverflow.com/a/22562045/1048572): the promise returned by `then` is *resolve*d with the return value of the `then` callback. Internally it basically uses the `new Promise`'s `resolve` callback like you did in your example, though of course `new Promise` is rarely if ever used for this directly. – Bergi Sep 29 '17 at 14:24
  • @SLaks Yes, that's what the OP wanted to know. But it would be nice to provide some reference. And no, promises are perfectly chainable without the behavior you describe. –  Sep 29 '17 at 14:58
  • 1
    @kuan: The point is that a promise will never resolve to another promise. It will always resolve to that promise's (eventual) value. – SLaks Sep 29 '17 at 15:02
  • _"The point is that a promise will never resolve to another promise. It will always resolve to that promise's (eventual) value."_ Is jQuery `$.Deferred()` implementation of `Promise` contrary to `Promise` specification? See https://stackoverflow.com/questions/46492277/does-jquery-deferred-have-a-bug-when-a-jquery-deferred-object-is-passed-to-defer – guest271314 Sep 29 '17 at 16:40
0

From MDN Promise().then() return value:

returns an already fulfilled promise, the promise returned by then gets fulfilled with that promise's value as its value.

If handler function return a Promise(), it will auto get the value


I have write an article to explain the return value of handler function more detail

If handler function ...

  • doesn't return value
var promise = Promise.resolve().then(() => {
  // no return;
});

promise.then((value) => {
  console.log(promise);
  console.log("value =", value);
});
Promise { <state>: "fulfilled", <value>: undefined }
value = undefined
  • return value that is not Promise
var promise = Promise.resolve().then(() => {
  return "value";
});

promise.then((value) => {
  console.log(promise);
  console.log("value =", value);
});
Promise { <state>: "fulfilled", <value>: "value" }
value = value
  • return Promise.resolve()
var promise = Promise.resolve().then(() => {
  return Promise.resolve("value");
});

promise.then((value) => {
  console.log(promise);
  console.log("value =", value);
});
Promise { <state>: "fulfilled", <value>: "value" }
value = value
  • return Promise.reject()
var promise = Promise.resolve().then(() => {
  return Promise.reject("value");
});

promise.catch((value) => {
  console.log(promise);
  console.log("value =", value);
});
Promise { <state>: "rejected", <reason>: "value" }
value = value
  • return pending Promise()
var promise = Promise.resolve().then(() => {
  return new Promise((resolve, reject) => {
    resolve("value");
  });
});

promise.then((value) => {
  console.log(promise);
  console.log("value =", value);
});
Promise { <state>: "fulfilled", <value>: "value" }
value = value
  • throws "error";
var promise = Promise.resolve().then(() => {
  throw "error";
});

promise.catch((value) => {
  console.log(promise);
  console.log("value =", value);
});
Promise { <state>: "rejected", <reason>: "error" }
value = error

Ref: Promise() handler function return value, MDN

Steely Wing
  • 16,239
  • 8
  • 58
  • 54