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.