why this code compiles
var Person = function() {
console.log("CALLED PERSON")};
Person.prototype.saySomething = function() {
console.log("saySomething PERSON")};
var ape = new Person();
ape.saySomething();
and this code throws error Cannot set property ‘saySomething’ of undefined
var Person = async function() {
console.log("CALLED PERSON")};
Person.prototype.saySomething = function() {
console.log("saySomething PERSON")};
var ape = new Person();
ape.saySomething();
3
When you use async function() {}
, you are declaring an asynchronous function
object. That’s different than a regular function
object. The asynchronous function object does not have a prototype.
So, when you try to do this:
var Person = async function() {
console.log("CALLED PERSON")
};
Person.prototype.saySomething = function() {
console.log("saySomething PERSON")
};
Person.prototype
is undefined
because there is no prototype
on an asynchronous function
object. Thus attempting to assign something to Person.prototype.saySomething
causes the error you see because Person.prototype
is undefined
.
There is some logic to this because an asynchronous function can’t be used as a constructor because an asynchronous function always returns a promise so it can’t ever return a new object as in let obj = new f()
. So, there’s no purpose in having a .prototype
property because it can’t be used that way.
If you really wanted to asynchronously create an object, you could always create an async
factory function that returns a promise that resolves with an object.
2
It is possible to add an async function in the end of the prototype chain.
Please notice that this works well in nodejs 8.11.1+.
// lets start with defining some cool async function who takes the time and
// awaits a promise
async function someCoolAsyncFunction() {
// this let will be returned after timeout in a different value.
let coolStuff = 'still boring';
// do something cool which takes time
await new Promise((resolve, reject) => setTimeout(() => {
coolStuff = 'an epiphany';
resolve();
}, 1000))
return coolStuff;
}
// Now let's define the 'regular' prototype chain with it's boring functions.
function Person(p) {
this.constructorPropery = p;
return this;
}
Person.prototype.notAsync = function() {
// do something regular in the prototype chain
console.log("Let's build some ",this.constructorPropery)
this.kindOfPerson = 'Regular and boring';
return this;
}
// And now, lets add an async function to this chain
Person.prototype.someAsyncFunction = async function() {
// you will still have access to 'this' in this function as long as the
// previous function in the prototype chain returnes 'this'
console.log('I used to be someone ',this.kindOfPerson);
// Now, this is our time to shine, lets await something cool
this.lifeChangingEvent = await someCoolAsyncFunction();
console.log('Until I had ',this.lifeChangingEvent);
a.kindOfPerson = 'enlightened';
console.log('and now I am ', a.kindOfPerson);
return this;
}
So this will work:
new Person('charachter').notAsync().someAsyncFunction();
But this WILL NOT work:
new Person('charachter').someAsyncFunction().notAsync();
And if you really need the data in ‘this’ outside the prototype chain you can also do:
let myself = new Person('charachter').notAsync();
console.log('myself.kindOfPerson is: ',myself.kindOfPerson);
myself.someAsyncFunction();
console.log('myself.kindOfPerson now is: ',myself.kindOfPerson);
Be sure to remember which prototype is an async function for which function or use your own naming convection for that.
1