3

JavaScript works strangely in the situation below.

I saw this answer and got this question about Javascript's weird behavior: https://stackoverflow.com/a/50173415/1614973

I found that if we change the then in his code to any other key name we will get a totally different result. CodePen demo: his, changed

I tried Chrome and Firefox and they all have this problem. I explorer the problem there and find some basic rules of this "bug".

// This one will always pending
const pendingPromise = Promise.resolve(x=>x).then(r => ({ then: y => 0 }));
pendingPromise.then(r => console.log("promise resolved")); // "promise resolved" will never logged

// Thanks @Jaromanda X's correction. a simpler version is:
const pendingPromise1 = Promise.resolve().then(() => ({then: y => 0}))
pendingPromise1.then(r => console.log("promise1 resolved")); // "promise1 resolved" will never logged

The pendingPromise is pending forever. There are three things to toggle this bug as far as I can see:

  1. The original one must be fulfilled with a function. (unnecessary constraint)
  2. In .then, an Object with a key of the name "then" must be returned.
  3. The value of the key then must be a function.

I'd like to get any clue about why this happens.

DDElephant
  • 33
  • 5
  • `x=>x` and `r` are total irrelevant, so point 1 is wrong ... `pendingPromise = Promise.resolve().then(()=>({then: y => 0}))` behaves identically – Jaromanda X Apr 20 '19 at 00:26
  • You may get some insight into your error by reading how then method should be implemented https://promisesaplus.com/#the-then-method – Jaromanda X Apr 20 '19 at 01:47
  • @Jaromanda X You are right. thanks for the correction. I've changed my question. – DDElephant Apr 20 '19 at 08:43

2 Answers2

1

The reason it never resolves is because your final "then" never calls resolve:

.then(r=>({then: y => 0})) just returns a non-thenable 0.

y is a resolve callback. To make your code work, change y to resolve and then call it. Or, just call y. Point is, until the resolve is called, the promise remains pending.

console.log("Starting Strange Promise")
const pendingPromise = Promise.resolve(x=>x).then(r=>(
  {then: y => "Strange Promise Done!"}
));
pendingPromise.then(console.log) // never happens

console.log("Starting Hacked Promise")
const hackedPromise = Promise.resolve(x=>x).then(r=>(
  {then: resolve => resolve("Hacked Promise Done!")}
));
hackedPromise.then(console.log)  // happens, like, speed of light quickly
Seph Reed
  • 8,797
  • 11
  • 60
  • 125
  • 1
    to avoid some confusion, it doesn't have to be called `resolve` ... it can be called anything, as it is the `onresolved` callback (one of two) that is passed to then ... therefore the original code would just need to be `{then: y => y(0)}` – Jaromanda X Apr 20 '19 at 00:28
1

We are all familiar with this: in one of the .then(callback), if the callback returns another promise, say lastPromise, the whole promise chain (up till that point) will effectively "become" lastPromise.

Internally, the promise chain doesn't check if the lastPromise thing is a real promise at all. It only check if it implements the thenable interface (has a .then() method). If it does, then it further assumes the method also conforms to the spec.

A promise must provide a then method to access its current or eventual value or reason.

A promise’s then method accepts two arguments:

promise.then(onFulfilled, onRejected)

Now you return an object from the callback that has a .then method, which makes it a thenable. The promise chain will thus treat this object like a comrade, believe that it is a "promise", call its .then(onFulfilled, onRejected) (which is effectively the (resolve, reject) pair).

Now a good thenable knows the right thing to do with (onFulfilled, onRejected). Unfortunately they trust the wrong guy. Your implementation goes like:

then = (onFulfilled) => 0

You never actually call onFulfilled(someValue) to resolve from pending state, so since the whole chain now "becomes" lastPromise, but your fake lastPromise fails to resolve/reject, the whole chain just stucks at pending state for good.

That's the sad story of blindly trust any thenable to be a promise.

Community
  • 1
  • 1
hackape
  • 18,643
  • 2
  • 29
  • 57