Strange Javascript: Promises and Prototypes
I recently wrote a blog about testing asynchronous functions in Javascript. I recalled how in my learning to code, the first time I encountered asynchronous functions was when writing a fetch request. That experience also introduced me to the idea of a javascript promise. Today I want to talk about one of those weird javascript interactions you might find yourself in while coding with promises.
The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value (MDN page here). Promises are always existing in one of three states:
- pending: initial state, neither fulfilled nor rejected.
- fulfilled: meaning that the operation was completed successfully.
- rejected: meaning that the operation failed.
For this weird interaction, we’re going to mainly look at what happens after our promise if fulfilled. To do this, we will use the resolve() method.
const theObject = {
"a": 7,
};const thePromise = new Promise((resolve, reject) => {
resolve(theObject);
});
thePromise.then(value => {
console.log(value === theObject); // -> true
console.log(value); // -> { a: 7 }
})
Here we have an object called theObject which has a key of “a” set to a value of 7. On the next line we create our promise. When the promise is fulfilled, it will be returned with theObject in tow. We then check to see if the object that was returned is equal to the original object. It is, and we can see that the value of value is the same as theObject. Nothing really too crazy here. Everything is working as it should. So what’s the deal, Kevin? You promised us some strange javascript…
Let’s see what happens when we try to resolve a promise with a promise instance.
const theObject = new Promise((resolve, reject) => {
resolve(7);
}); const thePromise = new Promise((resolve, reject) => {
resolve(theObject);
});
thePromise.then(value => {
console.log(value === theObject); // -> false
console.log(value); // -> 7
})
So if we just jump to the outputs at the bottom we can see that our value is no longer equal to whatever was stored inside theObject. So how come we’re getting different answers? Javascript, you silly thing. Let’s figure this out!
Some answers I’ve found recall back to the definition of how the resolve() method works. According to MDN “this function flattens nested layers of promise-like objects (e.g. a promise that resolves to a promise that resolves to something) into a single layer.” The above is certainly a promise that resolves to another promise.
I believe the key here lies in how an objects inheritance chain is set to the prototype property of its constructor. If that doesn’t make sense, no worries! Let’s check out the prototype data of both of our examples above and see what I mean.
Here is the prototype data for our first example where theObject is an object and not a promise. You can see that here our value after resolving is equal to theObject. You can also see how the constructor of this example is also an Object.
Here is our example when we’ve saved theObject to an instance of a new Promise. Already we can see why our values aren’t equal like they are in the above example. Our Promise has a constructor of a Promise and not an object.
Because the top level of our object inheritance chain is in one example an Object and in another example a Promise, we’re going to have different answers. Even if our promises still get resolved, our resolve() method cannot return a promise instance.
Why exactly do weird things like this happen? Who knows! Javascript is strange!