Home > database >  ES6 Promises strangely extract correct value out of custom js objects
ES6 Promises strangely extract correct value out of custom js objects

Time:11-17

I've written a library that is completely separate from Promise API but achieves similar goals. It uses window.requestAnimationFrame and falls back to setTimeout and has nothing common with Promises. In fact you can run it on ie9 - 10 or a machine from 2009 etc. Here is the source code

How is it possible that the below code works, and the 2nd promise gets the value (v 3) correctly into the 3rd promise after 10 second delay?? Because rafx.async... returns a custom proprietary object.

const x = Promise.resolve()
.then(() => console.log("2nd promise"))
.then(() => {
  //600 frames === 10 secs
  return rafx.async(6)
  .skipFrames(600)
  .then(v => v   1)
  .then(v => v   3);
});

console.log(x instanceof Promise);

x.then(v => console.log("3rd promise", v));
<script src="https://cdn.jsdelivr.net/npm/rafx"></script>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

The expectation would be that the v at x.then(v... would equal to whatever custom object returned from the custom then.

Here is what rafx.async(6).skipFrames(600)...then(v => v 3) returns:

prt.Thenable {....
    status: Status {thenable: prt.Thenable, instance: Rafx...
    _argObj: {value: 10, done: true},
    _breaker: {value: false}
    ....

The Thenable and Status constructors have nothing to do with Promises, they are completely custom.

To my surprise, this even works:

const x = Promise.resolve()
.then(() => console.log("2nd promise"))
.then(() => {
  return rafx.async("hello")
  .loop(function(str){
    return str;
  })
  .until(str => window.testVar === " world!")
  .then(str => str   window.testVar);
});

console.log(x instanceof Promise);

x.then((value) => console.log("3rd promise", value))
.catch((e) => console.log(e));
<script src="https://cdn.jsdelivr.net/npm/rafx"></script>
<button onclick="window.testVar = ' world!';">set testVar to ' world!'</button>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

You can verify that Promise.prototype.then !== rafx.Thenable.prototype.then, the then implementation is completely separate, as seen here;

So how is it possible that Promise understands how my API works????? (I am sure I am missing something awfully clear)

PS: I replaced all arrow functions (coz of this binding) with regular ones, it still works, but it shouldn't..

CodePudding user response:

The specification for Promises is designed so that it can interoperate with other "thenables". Ie, objects that have a .then property which is a function. If you resolve a promise with a thenable (in your case by returning a thenable in a .then block), the outer promise will call that function, passing in a resolve and reject function, and will wait for the inner thenable to call resolve. Only then will the outer promise resolve.

For example:

const promise = new Promise((resolve, reject) => {
  const myThenable = {
    then: (resolve2, reject2) => {
      console.log('running .then')
      setTimeout(() => {
        console.log('resolving inner');
        resolve2("hello");
      }, 1000)
    }
  }

  console.log('resolving outer');
  resolve(myThenable);
})

promise.then(result => {
  console.log('final result', result);
})
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Then code you've got for your thenable wasn't designed to work with this, but apparently it does:

prt.Thenable.prototype.then = function(f, rest){

When the promise calls your .then function, f will be the resolve function, and rest will be the reject function. Some point down the road you are calling f, which happens to have the behavior you want, and apparently it didn't cause any exceptions to have rest be a function that you weren't expecting.

  • Related