1

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>

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>

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..

ibrahim tanyalcin
  • 5,643
  • 3
  • 16
  • 22

1 Answers1

1

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);
})

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.

Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
  • This was such a weird coincidence. I accepted the answer. Is [this](https://promisesaplus.com/#:~:text=%E2%80%9Cpromise%E2%80%9D%20is%20an%20object%20or,thrown%20using%20the%20throw%20statement.) the correct reference to what you are referring?? – ibrahim tanyalcin Nov 16 '21 at 15:30
  • 1
    The Promises A+ spec used to be the spec for promises, back when they had to be done via custom libraries. So if you were using, say, [Bluebird](http://bluebirdjs.com/docs/getting-started.html) for your promises, it would follow Promises A+. Since 2015 though, javascript has built in promises, and their spec [can be found here](https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-promise-objects). Both specs do almost the same thing, and the old one can be easier to read, so you might prefer to look at Promises A+ even though it's older. – Nicholas Tower Nov 16 '21 at 15:37