Async function call inside a constructor in javascript class [duplicate] - javascript

At the moment, I'm attempting to use async/await within a class constructor function. This is so that I can get a custom e-mail tag for an Electron project I'm working on.
customElements.define('e-mail', class extends HTMLElement {
async constructor() {
super()
let uid = this.getAttribute('data-uid')
let message = await grabUID(uid)
const shadowRoot = this.attachShadow({mode: 'open'})
shadowRoot.innerHTML = `
<div id="email">A random email message has appeared. ${message}</div>
`
}
})
At the moment however, the project does not work, with the following error:
Class constructor may not be an async method
Is there a way to circumvent this so that I can use async/await within this? Instead of requiring callbacks or .then()?

This can never work.
The async keyword allows await to be used in a function marked as async but it also converts that function into a promise generator. So a function marked with async will return a promise. A constructor on the other hand returns the object it is constructing. Thus we have a situation where you want to both return an object and a promise: an impossible situation.
You can only use async/await where you can use promises because they are essentially syntax sugar for promises. You can't use promises in a constructor because a constructor must return the object to be constructed, not a promise.
There are two design patterns to overcome this, both invented before promises were around.
Use of an init() function. This works a bit like jQuery's .ready(). The object you create can only be used inside its own init or ready function:
Usage:
var myObj = new myClass();
myObj.init(function() {
// inside here you can use myObj
});
Implementation:
class myClass {
constructor () {
}
init (callback) {
// do something async and call the callback:
callback.bind(this)();
}
}
Use a builder. I've not seen this used much in javascript but this is one of the more common work-arounds in Java when an object needs to be constructed asynchronously. Of course, the builder pattern is used when constructing an object that requires a lot of complicated parameters. Which is exactly the use-case for asynchronous builders. The difference is that an async builder does not return an object but a promise of that object:
Usage:
myClass.build().then(function(myObj) {
// myObj is returned by the promise,
// not by the constructor
// or builder
});
// with async/await:
async function foo () {
var myObj = await myClass.build();
}
Implementation:
class myClass {
constructor (async_param) {
if (typeof async_param === 'undefined') {
throw new Error('Cannot be called directly');
}
}
static build () {
return doSomeAsyncStuff()
.then(function(async_result){
return new myClass(async_result);
});
}
}
Implementation with async/await:
class myClass {
constructor (async_param) {
if (typeof async_param === 'undefined') {
throw new Error('Cannot be called directly');
}
}
static async build () {
var async_result = await doSomeAsyncStuff();
return new myClass(async_result);
}
}
Note: although in the examples above we use promises for the async builder they are not strictly speaking necessary. You can just as easily write a builder that accept a callback.
Note on calling functions inside static functions.
This has nothing whatsoever to do with async constructors but with what the keyword this actually mean (which may be a bit surprising to people coming from languages that do auto-resolution of method names, that is, languages that don't need the this keyword).
The this keyword refers to the instantiated object. Not the class. Therefore you cannot normally use this inside static functions since the static function is not bound to any object but is bound directly to the class.
That is to say, in the following code:
class A {
static foo () {}
}
You cannot do:
var a = new A();
a.foo() // NOPE!!
instead you need to call it as:
A.foo();
Therefore, the following code would result in an error:
class A {
static foo () {
this.bar(); // you are calling this as static
// so bar is undefinned
}
bar () {}
}
To fix it you can make bar either a regular function or a static method:
function bar1 () {}
class A {
static foo () {
bar1(); // this is OK
A.bar2(); // this is OK
}
static bar2 () {}
}

You can definitely do this, by returning an Immediately Invoked Async Function Expression from the constructor. IIAFE is the fancy name for a very common pattern that was required in order to use await outside of an async function, before top-level await became available:
(async () => {
await someFunction();
})();
We'll be using this pattern to immediately execute the async function in the constructor, and return its result as this:
// Sample async function to be used in the async constructor
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
class AsyncConstructor {
constructor(value) {
return (async () => {
// Call async functions here
await sleep(500);
this.value = value;
// Constructors return `this` implicitly, but this is an IIFE, so
// return `this` explicitly (else we'd return an empty object).
return this;
})();
}
}
(async () => {
console.log('Constructing...');
const obj = await new AsyncConstructor(123);
console.log('Done:', obj);
})();
To instantiate the class, use:
const instance = await new AsyncConstructor(...);
For TypeScript, you need to assert that the type of the constructor is the class type, rather than a promise returning the class type:
class AsyncConstructor {
constructor(value) {
return (async (): Promise<AsyncConstructor> => {
// ...
return this;
})() as unknown as AsyncConstructor; // <-- type assertion
}
}
Downsides
Extending a class with an async constructor will have a limitation. If you need to call super in the constructor of the derived class, you'll have to call it without await. If you need to call the super constructor with await, you'll run into TypeScript error 2337: Super calls are not permitted outside constructors or in nested functions inside constructors.
It's been argued that it's a "bad practice" to have a constructor function return a Promise.
Before using this solution, determine whether you'll need to extend the class, and document that the constructor must be called with await.

Because async functions are promises, you can create a static function on your class which executes an async function which returns the instance of the class:
class Yql {
constructor () {
// Set up your class
}
static init () {
return (async function () {
let yql = new Yql()
// Do async stuff
await yql.build()
// Return instance
return yql
}())
}
async build () {
// Do stuff with await if needed
}
}
async function yql () {
// Do this instead of "new Yql()"
let yql = await Yql.init()
// Do stuff with yql instance
}
yql()
Call with let yql = await Yql.init() from an async function.

Unlike others have said, you can get it to work.
JavaScript classes can return literally anything from their constructor, even an instance of another class. So, you might return a Promise from the constructor of your class that resolves to its actual instance.
Below is an example:
export class Foo {
constructor() {
return (async () => {
// await anything you want
return this; // Return the newly-created instance
})();
}
}
Then, you'll create instances of Foo this way:
const foo = await new Foo();

The stopgap solution
You can create an async init() {... return this;} method, then instead do new MyClass().init() whenever you'd normally just say new MyClass().
This is not clean because it relies on everyone who uses your code, and yourself, to always instantiate the object like so. However if you're only using this object in a particular place or two in your code, it could maybe be fine.
A significant problem though occurs because ES has no type system, so if you forget to call it, you've just returned undefined because the constructor returns nothing. Oops. Much better would be to do something like:
The best thing to do would be:
class AsyncOnlyObject {
constructor() {
}
async init() {
this.someField = await this.calculateStuff();
}
async calculateStuff() {
return 5;
}
}
async function newAsync_AsyncOnlyObject() {
return await new AsyncOnlyObject().init();
}
newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}
The factory method solution (slightly better)
However then you might accidentally do new AsyncOnlyObject, you should probably just create factory function that uses Object.create(AsyncOnlyObject.prototype) directly:
async function newAsync_AsyncOnlyObject() {
return await Object.create(AsyncOnlyObject.prototype).init();
}
newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}
However say you want to use this pattern on many objects... you could abstract this as a decorator or something you (verbosely, ugh) call after defining like postProcess_makeAsyncInit(AsyncOnlyObject), but here I'm going to use extends because it sort of fits into subclass semantics (subclasses are parent class + extra, in that they should obey the design contract of the parent class, and may do additional things; an async subclass would be strange if the parent wasn't also async, because it could not be initialized the same way):
Abstracted solution (extends/subclass version)
class AsyncObject {
constructor() {
throw new Error('classes descended from AsyncObject must be initialized as (await) TheClassName.anew(), rather than new TheClassName()');
}
static async anew(...args) {
var R = Object.create(this.prototype);
R.init(...args);
return R;
}
}
class MyObject extends AsyncObject {
async init(x, y=5) {
this.x = x;
this.y = y;
// bonus: we need not return 'this'
}
}
MyObject.anew('x').then(console.log);
// output: MyObject {x: "x", y: 5}
(do not use in production: I have not thought through complicated scenarios such as whether this is the proper way to write a wrapper for keyword arguments.)

Based on your comments, you should probably do what every other HTMLElement with asset loading does: make the constructor start a sideloading action, generating a load or error event depending on the result.
Yes, that means using promises, but it also means "doing things the same way as every other HTML element", so you're in good company. For instance:
var img = new Image();
img.onload = function(evt) { ... }
img.addEventListener("load", evt => ... );
img.onerror = function(evt) { ... }
img.addEventListener("error", evt => ... );
img.src = "some url";
this kicks off an asynchronous load of the source asset that, when it succeeds, ends in onload and when it goes wrong, ends in onerror. So, make your own class do this too:
class EMailElement extends HTMLElement {
connectedCallback() {
this.uid = this.getAttribute('data-uid');
}
setAttribute(name, value) {
super.setAttribute(name, value);
if (name === 'data-uid') {
this.uid = value;
}
}
set uid(input) {
if (!input) return;
const uid = parseInt(input);
// don't fight the river, go with the flow, use a promise:
new Promise((resolve, reject) => {
yourDataBase.getByUID(uid, (err, result) => {
if (err) return reject(err);
resolve(result);
});
})
.then(result => {
this.renderLoaded(result.message);
})
.catch(error => {
this.renderError(error);
});
}
};
customElements.define('e-mail', EmailElement);
And then you make the renderLoaded/renderError functions deal with the event calls and shadow dom:
renderLoaded(message) {
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<div class="email">A random email message has appeared. ${message}</div>
`;
// is there an ancient event listener?
if (this.onload) {
this.onload(...);
}
// there might be modern event listeners. dispatch an event.
this.dispatchEvent(new Event('load'));
}
renderFailed() {
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<div class="email">No email messages.</div>
`;
// is there an ancient event listener?
if (this.onload) {
this.onerror(...);
}
// there might be modern event listeners. dispatch an event.
this.dispatchEvent(new Event('error'));
}
Also note I changed your id to a class, because unless you write some weird code to only ever allow a single instance of your <e-mail> element on a page, you can't use a unique identifier and then assign it to a bunch of elements.

I usually prefer a static async method that returns a new instance, but here's another way to do it. It's closer to literally awaiting a constructor. It works with TypeScript.
class Foo {
#promiseReady;
constructor() {
this.#promiseReady = this.#init();
}
async #init() {
await someAsyncStuff();
return this;
}
ready() {
return this.promiseReady;
}
}
let foo = await new Foo().ready();

I made this test-case based on #Downgoat's answer.
It runs on NodeJS.
This is Downgoat's code where the async part is provided by a setTimeout() call.
'use strict';
const util = require( 'util' );
class AsyncConstructor{
constructor( lapse ){
this.qqq = 'QQQ';
this.lapse = lapse;
return ( async ( lapse ) => {
await this.delay( lapse );
return this;
})( lapse );
}
async delay(ms) {
return await new Promise(resolve => setTimeout(resolve, ms));
}
}
let run = async ( millis ) => {
// Instatiate with await, inside an async function
let asyncConstructed = await new AsyncConstructor( millis );
console.log( 'AsyncConstructor: ' + util.inspect( asyncConstructed ));
};
run( 777 );
My use case is DAOs for the server-side of a web application.
As I see DAOs, they are each one associated to a record format, in my case a MongoDB collection like for instance a cook.
A cooksDAO instance holds a cook's data.
In my restless mind I would be able to instantiate a cook's DAO providing the cookId as an argument, and the instantiation would create the object and populate it with the cook's data.
Thus the need to run async stuff into the constructor.
I wanted to write:
let cook = new cooksDAO( '12345' );
to have available properties like cook.getDisplayName().
With this solution I have to do:
let cook = await new cooksDAO( '12345' );
which is very similar to the ideal.
Also, I need to do this inside an async function.
My B-plan was to leave the data loading out of the constructor, based on #slebetman suggestion to use an init function, and do something like this:
let cook = new cooksDAO( '12345' );
async cook.getData();
which doesn't break the rules.

Use the async method in constructor???
constructor(props) {
super(props);
(async () => await this.qwe(() => console.log(props), () => console.log(props)))();
}
async qwe(q, w) {
return new Promise((rs, rj) => {
rs(q());
rj(w());
});
}

If you can avoid extend, you can avoid classes all together and use function composition as constructors. You can use the variables in the scope instead of class members:
async function buildA(...) {
const data = await fetch(...);
return {
getData: function() {
return data;
}
}
}
and simple use it as
const a = await buildA(...);
If you're using typescript or flow, you can even enforce the interface of the constructors
Interface A {
getData: object;
}
async function buildA0(...): Promise<A> { ... }
async function buildA1(...): Promise<A> { ... }
...

I found myself in a situation like this and ended up using an IIFE
// using TypeScript
class SomeClass {
constructor() {
// do something here
}
doSomethingAsync(): SomeClass {
(async () => await asyncTask())();
return this;
}
}
const someClass = new SomeClass().doSomethingAsync();
If you have other tasks that are dependant on the async task you can run them after the IIFE completes its execution.

A lot of great knowledge here and some super() thoughtful responses. In short the technique outlined below is fairly straightforward, non-recursive, async-compatible and plays by the rules. More importantly I don't believe it has been properly covered here yet - though please correct me if wrong!
Instead of method calls we simply assign an II(A)FE to an instance prop:
// it's async-lite!
class AsyncLiteComponent {
constructor() {
// our instance includes a 'ready' property: an IIAFE promise
// that auto-runs our async needs and then resolves to the instance
// ...
// this is the primary difference to other answers, in that we defer
// from a property, not a method, and the async functionality both
// auto-runs and the promise/prop resolves to the instance
this.ready = (async () => {
// in this example we're auto-fetching something
this.msg = await AsyncLiteComponent.msg;
// we return our instance to allow nifty one-liners (see below)
return this;
})();
}
// we keep our async functionality in a static async getter
// ... technically (with some minor tweaks), we could prefetch
// or cache this response (but that isn't really our goal here)
static get msg() {
// yes I know - this example returns almost immediately (imagination people!)
return fetch('data:,Hello%20World%21').then((e) => e.text());
}
}
Seems simple enough, how is it used?
// Ok, so you *could* instantiate it the normal, excessively boring way
const iwillnotwait = new AsyncLiteComponent();
// and defer your waiting for later
await iwillnotwait.ready
console.log(iwillnotwait.msg)
// OR OR OR you can get all async/awaity about it!
const onlywhenimready = await new AsyncLiteComponent().ready;
console.log(onlywhenimready.msg)
// ... if you're really antsy you could even "pre-wait" using the static method,
// but you'd probably want some caching / update logic in the class first
const prefetched = await AsyncLiteComponent.msg;
// ... and I haven't fully tested this but it should also be open for extension
class Extensior extends AsyncLiteComponent {
constructor() {
super();
this.ready.then(() => console.log(this.msg))
}
}
const extendedwaittime = await new Extensior().ready;
Before posting I had a brief discussion on the viability of this technique in the comments of #slebetman's comprehensive answer. I wasn't entirely convinced by the outright dismissal, so thought I would open it up to further debate / tear down. Please do your worst :)

You can use Proxy's construct handle to do this, the code like this:
const SomeClass = new Proxy(class A {
constructor(user) {
this.user = user;
}
}, {
async construct(target, args, newTarget) {
const [name] = args;
// you can use await in here
const user = await fetch(name);
// invoke new A here
return new target(user);
}
});
const a = await new SomeClass('cage');
console.log(a.user); // user info

Variation on the builder pattern, using call():
function asyncMethod(arg) {
function innerPromise() { return new Promise((...)=> {...}) }
innerPromise().then(result => {
this.setStuff(result);
}
}
const getInstance = async (arg) => {
let instance = new Instance();
await asyncMethod.call(instance, arg);
return instance;
}

That can be done, like this:
class test
{
constructor () { return Promise.resolve (this); }
}
Or with real delays added, any asynchronous event, or setTimeout for instance:
class test
{
constructor ()
{
return new Promise ( (resolve, reject) =>
{ //doing something really delayed
setTimeout (resolve, 5, this);
});
}
doHello(a) {console.log("hello: " + a);}
}
async function main()
{
new test().then(a=> a.doHello("then")); //invoking asynchronously
console.log("testing"); //"testing" will be printed 5 seconds before "hello"
(await new test()).doHello("await"); //invoking synchronously
}
main();
In some cases, when involving inheritance, can't be returned Promise from base class constructor, it will mess with this pointer. First idea is adding function then to make the class promise alike, but it is problematic. My workaround is using a private variable #p retaining the promise, and function _then (instead of then) which should act on completion of #p
class basetest
{
#p = null;
constructor (){this.#p = Promise.resolve(this);}
async _then (func)
{
return this.#p.then ( (ths) =>
{
this.#p = null;
if (func) func (ths);
return this;
});
}
doHello(a) {console.log("hello: " + a);}
}
class test extends basetest
{
constructor(context)
{
super(context);
this.msg = "test: ";
}
doTest(a) {console.log(this.msg + a);}
}
async function main()
{
(await new test()._then()).doHello("await");//synchronous call
//note, now we use _then instead of then
(new test())._then(a=>{a.doHello("then");a.doTest("then");} );//asynchronous call
}
main();
And suppose a some webgl, with async image loading, generating a mesh and drawing a vertex array object:
class HeightMap extends GlVAObject
{
#vertices = [];
constructor (src, crossOrigin = "")
{
//super(theContextSetup);
let image = new Image();
image.src = src;
image.crossOrigin = crossOrigin;
return new Promise ( (resolve, reject) =>
{
image.addEventListener('load', () =>
{
//reading pixel values from image into this.#vertices
//and generate a heights map
//...
resolve(this);
} );
});
}
///...
}
function drawVao(vao) {/*do something*/}
async function main()
{
let vao = await new HeightMap ("./heightmaps/ArisonaCraterHeightMap.png");
drawVao(vao);
///...
}
/*
//version of main with asynchronous call
async function main()
{
new HeightMap ("./heightmaps/ArisonaCraterHeightMap.png").then (vao => drawVao(vao));
///...
}
*/
main();
Second variant with _then, as it really works in real life:
class HeightMap extends GlVAObject
{
#vertices = [];
constructor (src, crossOrigin = "")
{
//super(theContextSetup);
let image = new Image();
image.src = src;
image.crossOrigin = crossOrigin;
this.#p = Promise ( (resolve, reject) =>
{
image.addEventListener('load', () =>
{
//...
resolve(this);
} );
});
}
async _then (func)
{
return this.#p.then ( (ths) =>
{
this.#p = null;
if (func) func (ths);
return this;
});
}
///...
}
function drawVao(vao) {/*do something*/}
async function main()
{
new HeightMap ("./heightmaps/ArisonaCraterHeightMap.png")._then(vao => drawVao(vao) );
///...
}
/*
//version of main with synchronous call
async function main()
{
vao = (await new HeightMap ("./heightmaps/ArisonaCraterHeightMap.png"))._then();
drawVao(vao);
///...
}*/
main();

You may immediately invoke an anonymous async function that returns message and set it to the message variable. You might want to take a look at immediately invoked function expressions (IEFES), in case you are unfamiliar with this pattern. This will work like a charm.
var message = (async function() { return await grabUID(uid) })()

You should add then function to instance. Promise will recognize it as a thenable object with Promise.resolve automatically
const asyncSymbol = Symbol();
class MyClass {
constructor() {
this.asyncData = null
}
then(resolve, reject) {
return (this[asyncSymbol] = this[asyncSymbol] || new Promise((innerResolve, innerReject) => {
this.asyncData = { a: 1 }
setTimeout(() => innerResolve(this.asyncData), 3000)
})).then(resolve, reject)
}
}
async function wait() {
const asyncData = await new MyClass();
alert('run 3s later')
alert(asyncData.a)
}

#slebetmen's accepted answer explains well why this doesn't work. In addition to the two patterns presented in that answer, another option is to only access your async properties through a custom async getter. The constructor() can then trigger the async creation of the properties, but the getter then checks to see if the property is available before it uses or returns it.
This approach is particularly useful when you want to initialize a global object once on startup, and you want to do it inside a module. Instead of initializing in your index.js and passing the instance in the places that need it, simply require your module wherever the global object is needed.
Usage
const instance = new MyClass();
const prop = await instance.getMyProperty();
Implementation
class MyClass {
constructor() {
this.myProperty = null;
this.myPropertyPromise = this.downloadAsyncStuff();
}
async downloadAsyncStuff() {
// await yourAsyncCall();
this.myProperty = 'async property'; // this would instead by your async call
return this.myProperty;
}
getMyProperty() {
if (this.myProperty) {
return this.myProperty;
} else {
return this.myPropertyPromise;
}
}
}

The closest you can get to an asynchronous constructor is by waiting for it to finish executing if it hasn't already in all of its methods:
class SomeClass {
constructor() {
this.asyncConstructor = (async () => {
// Perform asynchronous operations here
})()
}
async someMethod() {
await this.asyncConstructor
// Perform normal logic here
}
}

The other answers are missing the obvious. Simply call an async function from your constructor:
constructor() {
setContentAsync();
}
async setContentAsync() {
let uid = this.getAttribute('data-uid')
let message = await grabUID(uid)
const shadowRoot = this.attachShadow({mode: 'open'})
shadowRoot.innerHTML = `
<div id="email">A random email message has appeared. ${message}</div>
`
}

Related

typescript class with constructor calling an internal async function that initiliaze a property [duplicate]

At the moment, I'm attempting to use async/await within a class constructor function. This is so that I can get a custom e-mail tag for an Electron project I'm working on.
customElements.define('e-mail', class extends HTMLElement {
async constructor() {
super()
let uid = this.getAttribute('data-uid')
let message = await grabUID(uid)
const shadowRoot = this.attachShadow({mode: 'open'})
shadowRoot.innerHTML = `
<div id="email">A random email message has appeared. ${message}</div>
`
}
})
At the moment however, the project does not work, with the following error:
Class constructor may not be an async method
Is there a way to circumvent this so that I can use async/await within this? Instead of requiring callbacks or .then()?
This can never work.
The async keyword allows await to be used in a function marked as async but it also converts that function into a promise generator. So a function marked with async will return a promise. A constructor on the other hand returns the object it is constructing. Thus we have a situation where you want to both return an object and a promise: an impossible situation.
You can only use async/await where you can use promises because they are essentially syntax sugar for promises. You can't use promises in a constructor because a constructor must return the object to be constructed, not a promise.
There are two design patterns to overcome this, both invented before promises were around.
Use of an init() function. This works a bit like jQuery's .ready(). The object you create can only be used inside its own init or ready function:
Usage:
var myObj = new myClass();
myObj.init(function() {
// inside here you can use myObj
});
Implementation:
class myClass {
constructor () {
}
init (callback) {
// do something async and call the callback:
callback.bind(this)();
}
}
Use a builder. I've not seen this used much in javascript but this is one of the more common work-arounds in Java when an object needs to be constructed asynchronously. Of course, the builder pattern is used when constructing an object that requires a lot of complicated parameters. Which is exactly the use-case for asynchronous builders. The difference is that an async builder does not return an object but a promise of that object:
Usage:
myClass.build().then(function(myObj) {
// myObj is returned by the promise,
// not by the constructor
// or builder
});
// with async/await:
async function foo () {
var myObj = await myClass.build();
}
Implementation:
class myClass {
constructor (async_param) {
if (typeof async_param === 'undefined') {
throw new Error('Cannot be called directly');
}
}
static build () {
return doSomeAsyncStuff()
.then(function(async_result){
return new myClass(async_result);
});
}
}
Implementation with async/await:
class myClass {
constructor (async_param) {
if (typeof async_param === 'undefined') {
throw new Error('Cannot be called directly');
}
}
static async build () {
var async_result = await doSomeAsyncStuff();
return new myClass(async_result);
}
}
Note: although in the examples above we use promises for the async builder they are not strictly speaking necessary. You can just as easily write a builder that accept a callback.
Note on calling functions inside static functions.
This has nothing whatsoever to do with async constructors but with what the keyword this actually mean (which may be a bit surprising to people coming from languages that do auto-resolution of method names, that is, languages that don't need the this keyword).
The this keyword refers to the instantiated object. Not the class. Therefore you cannot normally use this inside static functions since the static function is not bound to any object but is bound directly to the class.
That is to say, in the following code:
class A {
static foo () {}
}
You cannot do:
var a = new A();
a.foo() // NOPE!!
instead you need to call it as:
A.foo();
Therefore, the following code would result in an error:
class A {
static foo () {
this.bar(); // you are calling this as static
// so bar is undefinned
}
bar () {}
}
To fix it you can make bar either a regular function or a static method:
function bar1 () {}
class A {
static foo () {
bar1(); // this is OK
A.bar2(); // this is OK
}
static bar2 () {}
}
You can definitely do this, by returning an Immediately Invoked Async Function Expression from the constructor. IIAFE is the fancy name for a very common pattern that was required in order to use await outside of an async function, before top-level await became available:
(async () => {
await someFunction();
})();
We'll be using this pattern to immediately execute the async function in the constructor, and return its result as this:
// Sample async function to be used in the async constructor
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
class AsyncConstructor {
constructor(value) {
return (async () => {
// Call async functions here
await sleep(500);
this.value = value;
// Constructors return `this` implicitly, but this is an IIFE, so
// return `this` explicitly (else we'd return an empty object).
return this;
})();
}
}
(async () => {
console.log('Constructing...');
const obj = await new AsyncConstructor(123);
console.log('Done:', obj);
})();
To instantiate the class, use:
const instance = await new AsyncConstructor(...);
For TypeScript, you need to assert that the type of the constructor is the class type, rather than a promise returning the class type:
class AsyncConstructor {
constructor(value) {
return (async (): Promise<AsyncConstructor> => {
// ...
return this;
})() as unknown as AsyncConstructor; // <-- type assertion
}
}
Downsides
Extending a class with an async constructor will have a limitation. If you need to call super in the constructor of the derived class, you'll have to call it without await. If you need to call the super constructor with await, you'll run into TypeScript error 2337: Super calls are not permitted outside constructors or in nested functions inside constructors.
It's been argued that it's a "bad practice" to have a constructor function return a Promise.
Before using this solution, determine whether you'll need to extend the class, and document that the constructor must be called with await.
Because async functions are promises, you can create a static function on your class which executes an async function which returns the instance of the class:
class Yql {
constructor () {
// Set up your class
}
static init () {
return (async function () {
let yql = new Yql()
// Do async stuff
await yql.build()
// Return instance
return yql
}())
}
async build () {
// Do stuff with await if needed
}
}
async function yql () {
// Do this instead of "new Yql()"
let yql = await Yql.init()
// Do stuff with yql instance
}
yql()
Call with let yql = await Yql.init() from an async function.
Unlike others have said, you can get it to work.
JavaScript classes can return literally anything from their constructor, even an instance of another class. So, you might return a Promise from the constructor of your class that resolves to its actual instance.
Below is an example:
export class Foo {
constructor() {
return (async () => {
// await anything you want
return this; // Return the newly-created instance
})();
}
}
Then, you'll create instances of Foo this way:
const foo = await new Foo();
The stopgap solution
You can create an async init() {... return this;} method, then instead do new MyClass().init() whenever you'd normally just say new MyClass().
This is not clean because it relies on everyone who uses your code, and yourself, to always instantiate the object like so. However if you're only using this object in a particular place or two in your code, it could maybe be fine.
A significant problem though occurs because ES has no type system, so if you forget to call it, you've just returned undefined because the constructor returns nothing. Oops. Much better would be to do something like:
The best thing to do would be:
class AsyncOnlyObject {
constructor() {
}
async init() {
this.someField = await this.calculateStuff();
}
async calculateStuff() {
return 5;
}
}
async function newAsync_AsyncOnlyObject() {
return await new AsyncOnlyObject().init();
}
newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}
The factory method solution (slightly better)
However then you might accidentally do new AsyncOnlyObject, you should probably just create factory function that uses Object.create(AsyncOnlyObject.prototype) directly:
async function newAsync_AsyncOnlyObject() {
return await Object.create(AsyncOnlyObject.prototype).init();
}
newAsync_AsyncOnlyObject().then(console.log);
// output: AsyncOnlyObject {someField: 5}
However say you want to use this pattern on many objects... you could abstract this as a decorator or something you (verbosely, ugh) call after defining like postProcess_makeAsyncInit(AsyncOnlyObject), but here I'm going to use extends because it sort of fits into subclass semantics (subclasses are parent class + extra, in that they should obey the design contract of the parent class, and may do additional things; an async subclass would be strange if the parent wasn't also async, because it could not be initialized the same way):
Abstracted solution (extends/subclass version)
class AsyncObject {
constructor() {
throw new Error('classes descended from AsyncObject must be initialized as (await) TheClassName.anew(), rather than new TheClassName()');
}
static async anew(...args) {
var R = Object.create(this.prototype);
R.init(...args);
return R;
}
}
class MyObject extends AsyncObject {
async init(x, y=5) {
this.x = x;
this.y = y;
// bonus: we need not return 'this'
}
}
MyObject.anew('x').then(console.log);
// output: MyObject {x: "x", y: 5}
(do not use in production: I have not thought through complicated scenarios such as whether this is the proper way to write a wrapper for keyword arguments.)
Based on your comments, you should probably do what every other HTMLElement with asset loading does: make the constructor start a sideloading action, generating a load or error event depending on the result.
Yes, that means using promises, but it also means "doing things the same way as every other HTML element", so you're in good company. For instance:
var img = new Image();
img.onload = function(evt) { ... }
img.addEventListener("load", evt => ... );
img.onerror = function(evt) { ... }
img.addEventListener("error", evt => ... );
img.src = "some url";
this kicks off an asynchronous load of the source asset that, when it succeeds, ends in onload and when it goes wrong, ends in onerror. So, make your own class do this too:
class EMailElement extends HTMLElement {
connectedCallback() {
this.uid = this.getAttribute('data-uid');
}
setAttribute(name, value) {
super.setAttribute(name, value);
if (name === 'data-uid') {
this.uid = value;
}
}
set uid(input) {
if (!input) return;
const uid = parseInt(input);
// don't fight the river, go with the flow, use a promise:
new Promise((resolve, reject) => {
yourDataBase.getByUID(uid, (err, result) => {
if (err) return reject(err);
resolve(result);
});
})
.then(result => {
this.renderLoaded(result.message);
})
.catch(error => {
this.renderError(error);
});
}
};
customElements.define('e-mail', EmailElement);
And then you make the renderLoaded/renderError functions deal with the event calls and shadow dom:
renderLoaded(message) {
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<div class="email">A random email message has appeared. ${message}</div>
`;
// is there an ancient event listener?
if (this.onload) {
this.onload(...);
}
// there might be modern event listeners. dispatch an event.
this.dispatchEvent(new Event('load'));
}
renderFailed() {
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<div class="email">No email messages.</div>
`;
// is there an ancient event listener?
if (this.onload) {
this.onerror(...);
}
// there might be modern event listeners. dispatch an event.
this.dispatchEvent(new Event('error'));
}
Also note I changed your id to a class, because unless you write some weird code to only ever allow a single instance of your <e-mail> element on a page, you can't use a unique identifier and then assign it to a bunch of elements.
I usually prefer a static async method that returns a new instance, but here's another way to do it. It's closer to literally awaiting a constructor. It works with TypeScript.
class Foo {
#promiseReady;
constructor() {
this.#promiseReady = this.#init();
}
async #init() {
await someAsyncStuff();
return this;
}
ready() {
return this.promiseReady;
}
}
let foo = await new Foo().ready();
I made this test-case based on #Downgoat's answer.
It runs on NodeJS.
This is Downgoat's code where the async part is provided by a setTimeout() call.
'use strict';
const util = require( 'util' );
class AsyncConstructor{
constructor( lapse ){
this.qqq = 'QQQ';
this.lapse = lapse;
return ( async ( lapse ) => {
await this.delay( lapse );
return this;
})( lapse );
}
async delay(ms) {
return await new Promise(resolve => setTimeout(resolve, ms));
}
}
let run = async ( millis ) => {
// Instatiate with await, inside an async function
let asyncConstructed = await new AsyncConstructor( millis );
console.log( 'AsyncConstructor: ' + util.inspect( asyncConstructed ));
};
run( 777 );
My use case is DAOs for the server-side of a web application.
As I see DAOs, they are each one associated to a record format, in my case a MongoDB collection like for instance a cook.
A cooksDAO instance holds a cook's data.
In my restless mind I would be able to instantiate a cook's DAO providing the cookId as an argument, and the instantiation would create the object and populate it with the cook's data.
Thus the need to run async stuff into the constructor.
I wanted to write:
let cook = new cooksDAO( '12345' );
to have available properties like cook.getDisplayName().
With this solution I have to do:
let cook = await new cooksDAO( '12345' );
which is very similar to the ideal.
Also, I need to do this inside an async function.
My B-plan was to leave the data loading out of the constructor, based on #slebetman suggestion to use an init function, and do something like this:
let cook = new cooksDAO( '12345' );
async cook.getData();
which doesn't break the rules.
Use the async method in constructor???
constructor(props) {
super(props);
(async () => await this.qwe(() => console.log(props), () => console.log(props)))();
}
async qwe(q, w) {
return new Promise((rs, rj) => {
rs(q());
rj(w());
});
}
If you can avoid extend, you can avoid classes all together and use function composition as constructors. You can use the variables in the scope instead of class members:
async function buildA(...) {
const data = await fetch(...);
return {
getData: function() {
return data;
}
}
}
and simple use it as
const a = await buildA(...);
If you're using typescript or flow, you can even enforce the interface of the constructors
Interface A {
getData: object;
}
async function buildA0(...): Promise<A> { ... }
async function buildA1(...): Promise<A> { ... }
...
I found myself in a situation like this and ended up using an IIFE
// using TypeScript
class SomeClass {
constructor() {
// do something here
}
doSomethingAsync(): SomeClass {
(async () => await asyncTask())();
return this;
}
}
const someClass = new SomeClass().doSomethingAsync();
If you have other tasks that are dependant on the async task you can run them after the IIFE completes its execution.
A lot of great knowledge here and some super() thoughtful responses. In short the technique outlined below is fairly straightforward, non-recursive, async-compatible and plays by the rules. More importantly I don't believe it has been properly covered here yet - though please correct me if wrong!
Instead of method calls we simply assign an II(A)FE to an instance prop:
// it's async-lite!
class AsyncLiteComponent {
constructor() {
// our instance includes a 'ready' property: an IIAFE promise
// that auto-runs our async needs and then resolves to the instance
// ...
// this is the primary difference to other answers, in that we defer
// from a property, not a method, and the async functionality both
// auto-runs and the promise/prop resolves to the instance
this.ready = (async () => {
// in this example we're auto-fetching something
this.msg = await AsyncLiteComponent.msg;
// we return our instance to allow nifty one-liners (see below)
return this;
})();
}
// we keep our async functionality in a static async getter
// ... technically (with some minor tweaks), we could prefetch
// or cache this response (but that isn't really our goal here)
static get msg() {
// yes I know - this example returns almost immediately (imagination people!)
return fetch('data:,Hello%20World%21').then((e) => e.text());
}
}
Seems simple enough, how is it used?
// Ok, so you *could* instantiate it the normal, excessively boring way
const iwillnotwait = new AsyncLiteComponent();
// and defer your waiting for later
await iwillnotwait.ready
console.log(iwillnotwait.msg)
// OR OR OR you can get all async/awaity about it!
const onlywhenimready = await new AsyncLiteComponent().ready;
console.log(onlywhenimready.msg)
// ... if you're really antsy you could even "pre-wait" using the static method,
// but you'd probably want some caching / update logic in the class first
const prefetched = await AsyncLiteComponent.msg;
// ... and I haven't fully tested this but it should also be open for extension
class Extensior extends AsyncLiteComponent {
constructor() {
super();
this.ready.then(() => console.log(this.msg))
}
}
const extendedwaittime = await new Extensior().ready;
Before posting I had a brief discussion on the viability of this technique in the comments of #slebetman's comprehensive answer. I wasn't entirely convinced by the outright dismissal, so thought I would open it up to further debate / tear down. Please do your worst :)
You can use Proxy's construct handle to do this, the code like this:
const SomeClass = new Proxy(class A {
constructor(user) {
this.user = user;
}
}, {
async construct(target, args, newTarget) {
const [name] = args;
// you can use await in here
const user = await fetch(name);
// invoke new A here
return new target(user);
}
});
const a = await new SomeClass('cage');
console.log(a.user); // user info
Variation on the builder pattern, using call():
function asyncMethod(arg) {
function innerPromise() { return new Promise((...)=> {...}) }
innerPromise().then(result => {
this.setStuff(result);
}
}
const getInstance = async (arg) => {
let instance = new Instance();
await asyncMethod.call(instance, arg);
return instance;
}
That can be done, like this:
class test
{
constructor () { return Promise.resolve (this); }
}
Or with real delays added, any asynchronous event, or setTimeout for instance:
class test
{
constructor ()
{
return new Promise ( (resolve, reject) =>
{ //doing something really delayed
setTimeout (resolve, 5, this);
});
}
doHello(a) {console.log("hello: " + a);}
}
async function main()
{
new test().then(a=> a.doHello("then")); //invoking asynchronously
console.log("testing"); //"testing" will be printed 5 seconds before "hello"
(await new test()).doHello("await"); //invoking synchronously
}
main();
In some cases, when involving inheritance, can't be returned Promise from base class constructor, it will mess with this pointer. First idea is adding function then to make the class promise alike, but it is problematic. My workaround is using a private variable #p retaining the promise, and function _then (instead of then) which should act on completion of #p
class basetest
{
#p = null;
constructor (){this.#p = Promise.resolve(this);}
async _then (func)
{
return this.#p.then ( (ths) =>
{
this.#p = null;
if (func) func (ths);
return this;
});
}
doHello(a) {console.log("hello: " + a);}
}
class test extends basetest
{
constructor(context)
{
super(context);
this.msg = "test: ";
}
doTest(a) {console.log(this.msg + a);}
}
async function main()
{
(await new test()._then()).doHello("await");//synchronous call
//note, now we use _then instead of then
(new test())._then(a=>{a.doHello("then");a.doTest("then");} );//asynchronous call
}
main();
And suppose a some webgl, with async image loading, generating a mesh and drawing a vertex array object:
class HeightMap extends GlVAObject
{
#vertices = [];
constructor (src, crossOrigin = "")
{
//super(theContextSetup);
let image = new Image();
image.src = src;
image.crossOrigin = crossOrigin;
return new Promise ( (resolve, reject) =>
{
image.addEventListener('load', () =>
{
//reading pixel values from image into this.#vertices
//and generate a heights map
//...
resolve(this);
} );
});
}
///...
}
function drawVao(vao) {/*do something*/}
async function main()
{
let vao = await new HeightMap ("./heightmaps/ArisonaCraterHeightMap.png");
drawVao(vao);
///...
}
/*
//version of main with asynchronous call
async function main()
{
new HeightMap ("./heightmaps/ArisonaCraterHeightMap.png").then (vao => drawVao(vao));
///...
}
*/
main();
Second variant with _then, as it really works in real life:
class HeightMap extends GlVAObject
{
#vertices = [];
constructor (src, crossOrigin = "")
{
//super(theContextSetup);
let image = new Image();
image.src = src;
image.crossOrigin = crossOrigin;
this.#p = Promise ( (resolve, reject) =>
{
image.addEventListener('load', () =>
{
//...
resolve(this);
} );
});
}
async _then (func)
{
return this.#p.then ( (ths) =>
{
this.#p = null;
if (func) func (ths);
return this;
});
}
///...
}
function drawVao(vao) {/*do something*/}
async function main()
{
new HeightMap ("./heightmaps/ArisonaCraterHeightMap.png")._then(vao => drawVao(vao) );
///...
}
/*
//version of main with synchronous call
async function main()
{
vao = (await new HeightMap ("./heightmaps/ArisonaCraterHeightMap.png"))._then();
drawVao(vao);
///...
}*/
main();
You may immediately invoke an anonymous async function that returns message and set it to the message variable. You might want to take a look at immediately invoked function expressions (IEFES), in case you are unfamiliar with this pattern. This will work like a charm.
var message = (async function() { return await grabUID(uid) })()
You should add then function to instance. Promise will recognize it as a thenable object with Promise.resolve automatically
const asyncSymbol = Symbol();
class MyClass {
constructor() {
this.asyncData = null
}
then(resolve, reject) {
return (this[asyncSymbol] = this[asyncSymbol] || new Promise((innerResolve, innerReject) => {
this.asyncData = { a: 1 }
setTimeout(() => innerResolve(this.asyncData), 3000)
})).then(resolve, reject)
}
}
async function wait() {
const asyncData = await new MyClass();
alert('run 3s later')
alert(asyncData.a)
}
#slebetmen's accepted answer explains well why this doesn't work. In addition to the two patterns presented in that answer, another option is to only access your async properties through a custom async getter. The constructor() can then trigger the async creation of the properties, but the getter then checks to see if the property is available before it uses or returns it.
This approach is particularly useful when you want to initialize a global object once on startup, and you want to do it inside a module. Instead of initializing in your index.js and passing the instance in the places that need it, simply require your module wherever the global object is needed.
Usage
const instance = new MyClass();
const prop = await instance.getMyProperty();
Implementation
class MyClass {
constructor() {
this.myProperty = null;
this.myPropertyPromise = this.downloadAsyncStuff();
}
async downloadAsyncStuff() {
// await yourAsyncCall();
this.myProperty = 'async property'; // this would instead by your async call
return this.myProperty;
}
getMyProperty() {
if (this.myProperty) {
return this.myProperty;
} else {
return this.myPropertyPromise;
}
}
}
The closest you can get to an asynchronous constructor is by waiting for it to finish executing if it hasn't already in all of its methods:
class SomeClass {
constructor() {
this.asyncConstructor = (async () => {
// Perform asynchronous operations here
})()
}
async someMethod() {
await this.asyncConstructor
// Perform normal logic here
}
}
The other answers are missing the obvious. Simply call an async function from your constructor:
constructor() {
setContentAsync();
}
async setContentAsync() {
let uid = this.getAttribute('data-uid')
let message = await grabUID(uid)
const shadowRoot = this.attachShadow({mode: 'open'})
shadowRoot.innerHTML = `
<div id="email">A random email message has appeared. ${message}</div>
`
}

Mock a top level import of a class with an internal method I also need to mock with jest.fn() for a test- no __mocks__ [duplicate]

Let's suppose I have the following class:
export default class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
sayMyName() {
console.log(this.first + " " + this.last);
}
bla() {
return "bla";
}
}
Suppose I want to create a mocked class where method 'sayMyName' will be mocked and method 'bla' will stay as is.
The test I wrote is:
const Person = require("../Person");
jest.mock('../Person', () => {
return jest.fn().mockImplementation(() => {
return {sayMyName: () => {
return 'Hello'
}};
});
});
let person = new Person();
test('MyTest', () => {
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
})
The first 'expect' statement passes, which means that 'sayMyName' was mocked successfully. But, the second 'expect' fails with the error:
TypeError: person.bla is not a function
I understand that the mocked class erased all methods.
I want to know how to mock a class such that only specific method(s) will be mocked.
Using jest.spyOn() is the proper Jest way of mocking a single method and leaving the rest be. Actually there are two slightly different approaches to this.
1. Modify the method only in a single object
import Person from "./Person";
test('Modify only instance', () => {
let person = new Person('Lorem', 'Ipsum');
let spy = jest.spyOn(person, 'sayMyName').mockImplementation(() => 'Hello');
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
// unnecessary in this case, putting it here just to illustrate how to "unmock" a method
spy.mockRestore();
});
2. Modify the class itself, so that all the instances are affected
import Person from "./Person";
beforeAll(() => {
jest.spyOn(Person.prototype, 'sayMyName').mockImplementation(() => 'Hello');
});
afterAll(() => {
jest.restoreAllMocks();
});
test('Modify class', () => {
let person = new Person('Lorem', 'Ipsum');
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
});
And for the sake of completeness, this is how you'd mock a static method:
jest.spyOn(Person, 'myStaticMethod').mockImplementation(() => 'blah');
Edit 05/03/2021
I see a number of people disagree with the below approach, and that's cool. I do have a slight disagreement with #blade's approach, though, in that it actually doesn't test the class because it's using mockImplementation. If the class changes, the tests will still always pass giving false positives. So here's an example with spyOn.
// person.js
export default class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
sayMyName() {
return this.first + " " + this.last; // Adjusted to return a value
}
bla() {
return "bla";
}
}
and the test:
import Person from './'
describe('Person class', () => {
const person = new Person('Guy', 'Smiley')
// Spying on the actual methods of the Person class
jest.spyOn(person, 'sayMyName')
jest.spyOn(person, 'bla')
it('should return out the first and last name', () => {
expect(person.sayMyName()).toEqual('Guy Smiley') // deterministic
expect(person.sayMyName).toHaveBeenCalledTimes(1)
});
it('should return bla when blah is called', () => {
expect(person.bla()).toEqual('bla')
expect(person.bla).toHaveBeenCalledTimes(1)
})
});
Cheers! 🍻
I don't see how the mocked implementation actually solves anything for you. I think this makes a bit more sense
import Person from "./Person";
describe("Person", () => {
it("should...", () => {
const sayMyName = Person.prototype.sayMyName = jest.fn();
const person = new Person('guy', 'smiley');
const expected = {
first: 'guy',
last: 'smiley'
}
person.sayMyName();
expect(sayMyName).toHaveBeenCalledTimes(1);
expect(person).toEqual(expected);
});
});
Not really answer the question, but I want to show a use case where you want to mock a dependent class to verify another class.
For example: Foo depends on Bar. Internally Foo created an instance of Bar. You want to mock Bar for testing Foo.
Bar class
class Bar {
public runBar(): string {
return 'Real bar';
}
}
export default Bar;
Foo class
import Bar from './Bar';
class Foo {
private bar: Bar;
constructor() {
this.bar = new Bar();
}
public runFoo(): string {
return 'real foo : ' + this.bar.runBar();
}
}
export default Foo;
The test:
import Foo from './Foo';
import Bar from './Bar';
jest.mock('./Bar');
describe('Foo', () => {
it('should return correct foo', () => {
// As Bar is already mocked,
// we just need to cast it to jest.Mock (for TypeScript) and mock whatever you want
(Bar.prototype.runBar as jest.Mock).mockReturnValue('Mocked bar');
const foo = new Foo();
expect(foo.runFoo()).toBe('real foo : Mocked bar');
});
});
Note: this will not work if you use arrow functions to define methods in your class (as they are difference between instances). Converting it to regular instance method would make it work.
See also jest.requireActual(moduleName)
Have been asking similar question and I think figured out a solution. This should work no matter where Person class instance is actually used.
const Person = require("../Person");
jest.mock("../Person", function () {
const { default: mockRealPerson } = jest.requireActual('../Person');
mockRealPerson.prototype.sayMyName = function () {
return "Hello";
}
return mockRealPerson
});
test('MyTest', () => {
const person = new Person();
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
});
rather than mocking the class you could extend it like this:
class MockedPerson extends Person {
sayMyName () {
return 'Hello'
}
}
// and then
let person = new MockedPerson();
If you are using Typescript, you can do the following:
Person.prototype.sayMyName = jest.fn().mockImplementationOnce(async () =>
await 'my name is dev'
);
And in your test, you can do something like this:
const person = new Person();
const res = await person.sayMyName();
expect(res).toEqual('my name is dev');
Hope this helps someone!
I've combined both #sesamechicken and #Billy Reilly answers to create a util function that mock (one or more) specific methods of a class, without definitely impacting the class itself.
/**
* #CrazySynthax class, a tiny bit updated to be able to easily test the mock.
*/
class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
sayMyName() {
return this.first + " " + this.last + this.yourGodDamnRight();
}
yourGodDamnRight() {
return ", you're god damn right";
}
}
/**
* Return a new class, with some specific methods mocked.
*
* We have to create a new class in order to avoid altering the prototype of the class itself, which would
* most likely impact other tests.
*
* #param Klass: The class to mock
* #param functionNames: A string or a list of functions names to mock.
* #returns {Class} a new class.
*/
export function mockSpecificMethods(Klass, functionNames) {
if (!Array.isArray(functionNames))
functionNames = [functionNames];
class MockedKlass extends Klass {
}
const functionNamesLenght = functionNames.length;
for (let index = 0; index < functionNamesLenght; ++index) {
let name = functionNames[index];
MockedKlass.prototype[name] = jest.fn();
};
return MockedKlass;
}
/**
* Making sure it works
*/
describe('Specific Mocked function', () => {
it('mocking sayMyName', () => {
const walter = new (mockSpecificMethods(Person, 'yourGodDamnRight'))('walter', 'white');
walter.yourGodDamnRight.mockReturnValue(", that's correct"); // yourGodDamnRight is now a classic jest mock;
expect(walter.sayMyName()).toBe("walter white, that's correct");
expect(walter.yourGodDamnRight.mock.calls.length).toBe(1);
// assert that Person is not impacted.
const saul = new Person('saul', 'goodman');
expect(saul.sayMyName()).toBe("saul goodman, you're god damn right");
});
});
I was trying to get this to work on a class that had already been mocked. Because it had been mocked already, there was no prototype available for me to modify, so I found this workaround.
I don't love this solution, so if anyone knows a better way to update a method to a class that has already been mocked out, I'm all ears.
And just to clarify, the main answers to this question are working with classes that are not mocked out. In my situation, the class has already been mocked out and I'm trying to update one of the methods to the already-mocked class.
My solution:
const previousClassInstance = new PreviouslyMockedClass();
PreviouslyMockedClass.mockImplementation(() => {
return {
// "Import" the previous class methods at the top
...previousClassInstance,
// Then overwrite the ones you wanna update
myUpdatedMethod: jest.fn(() => {
console.log(
"This method is updated, the others are present and unaltered"
);
}),
};
});
I found a way to reproduce the original spyOn behaviour with Typescript and ES6 modules since you get a jest-Error nowadays when you try to use it on a class instance method.
const addTodoSpy = jest.spyOn(storeThatNeedsToBeSpiedOn, 'addTodo');
TypeError: Cannot redefine property: addTodo at Function.defineProperty (<anonymous>)
The advantage of spyOn is that the original method still runs in its original implementation.
In my case the class instance is a mobX store. But I see no reason why it shouldnt work for other class modules.
The way to do it is simply to save a copy of the original method, then creating a mock function with the saved copy as mockImplemtation and the saving this back into the class instance
const storeThatNeedsToBeSpiedOn = new TodoStore();
const keep = storeThatNeedsToBeSpiedOn.addTodo;
const addTodoSpy = jest.fn().mockImplementation(keep);
storeThatNeedsToBeSpiedOn.addTodo = addTodoSpy;
const storeToTest = new SomeOtherStore(storeThatNeedsToBeSpiedOn);
and in the test:
storeToTest.methodThatCallsAddTodoInternally();
expect(addTodoSpy).toBeCalledTimes(1);
the beauty of this is, that the original implementation of the method still runs with all its side effects (if there are any). So you could finish off your test by saying;
expect(storeThatNeedsToBeSpiedOn.todos.length).toEqual(/* one more than before */);
Hope this helps someone out there who is as frustrated as i was ;)

How to inject conditional checks before execution of any class methods

I'm looking for some opinions / solutions on how to handle async data retrieval gracefully.
In initializing any class with some data asynchronously, I've been taking the approach like so:
class SomeClass {
// Turning off ts compiler's strictPropertyInitialization
private someProperty: SomeType
public async init(): Promise<this> {
this.someProperty = await goAndGetDataFromWhoKnowsWhere();
return this;
}
public async aMethod(): Promise<AType> {
// do its thing
}
public async anotherMethod(): Promise<AnotherType> {
// do its thing
}
}
And expect the user (myself / another co-worker) to use this class like so:
const someResult = new SomeClass()
.init()
.then( thatClass => thatClass.aMethod() )
This approach sure serves the purpose okay, but there're no hard restrictions to make sure the init() is getting called. Sometimes when someone forgets about it things break.
We could probably turn on the strictPropertyInitialization and inject checking in every single class method. That'd work for sure but the identical lines in the methods are yelling that there's probably a better way.
class SomeClass {
private someProperty: SomeType | undefined // To enforce null-checking
public async init(): Promise<this> {
this.someProperty = await goAndGetDataFromWhoKnowsWhere();
return this;
}
public async aMethod(): Promise<AType> {
if (!this.someProperty) await this.init();
// do its thing
}
public async anotherMethod(): Promise<AnotherType> {
if (!this.someProperty) await this.init();
// do its thing
}
}
Are there any solutions to this problem? Any design patterns that work around this problem? Helps appreciated! :)
Have you thought about not exposing the new() constructor call at all? What if you make the constructor private, and expose a static init() method that asynchronously constructs an instance and fills it with the data:
class SomeClass {
static async init(): Promise<SomeClass> {
return new SomeClass(await goAndGetDataFromWhoKnowsWhere());
}
private constructor(private someProperty: SomeType) { }
// your other methods
}
new SomeClass("oops"); // can't do this
SomeClass.init().then(thatClass => thatClass.aMethod());
Now it's essentially impossible for anyone to use this the wrong way. Hope that gives you some ideas. Good luck!
Another option is you could wrap creation of the class in a function. Assuming init must be called on every instance, you could just handle that on creation:
(Sorry it's not in TypeScript; I'm just not as familiar with it.)
const goAndGetDataFromWhoKnowsWhere = async () => 123;
const SomeClass = () => {
class SomeClass {
async init() {
this.someProperty = await goAndGetDataFromWhoKnowsWhere();
return this;
}
}
return new SomeClass().init();
};
SomeClass().then(inst => {
console.log('someProperty:', inst.someProperty);
});
Similar to jcalz's answer this would not allow use with the new keyword:
new SomeClass(); // TypeError: SomeClass is not a constructor
How about just using a function instead?
function SomeClass(){
var newObj = Object.create(/* your prototype */)
return goAndGetDataFromWhoKnowsWhere()
.then((data) => {
newObj.someProperty = data;
return newObj;
})
}
SomeClass().then((newObj) => {})

Asynchronous operations in constructor

Hey I have question about prototype and inheretience in functions. Could you explan me how I can return arr from constructor and add this arr to prototype?
var example = new Constructor()
function Constructor(){
Service.getService().then(function(data){
this.arr = data.data.array;
return this.arr
})
}
Constructor.prototype.getArray = function(){
console.log(this.arr)
})
example.getArray();
And in getArray this.arr is undefined. Service and getService() are angular factory and connection between front and back-end
It is particularly difficult to put asynchronous operations in a constructor. This is for several reasons:
The constructor needs to return the newly created object so it can't return a promise that would tell you when the async operation is done.
If you do an asynchronous operation inside the constructor that sets some instance data and the constructor returns the object, then you have no way for the calling code to know when the async operation is actually done.
For these reasons, you usually don't want to do an async operation inside a constructor. IMO, the cleanest architecture below is the factory function that returns a promise that resolves to your finished object. You can do as much asynchronous stuff as you want in the factory function (call any methods on the object) and you don't expose the object to the caller until it is fully formed.
These are some of the various options for dealing with that issue:
Use Factory Function that Returns a Promise
This uses a factory function that does some of the more common work for you. It also doesn't reveal the new object until its fully initialized which is a good programming practice as the caller can't accidentally try to use a partially formed object in which the asynchronous stuff hasn't finished yet. The factory function option also cleanly propagates errors (either synchronous or asynchronous) by rejecting the returned promise:
// don't make this class definition public so the constructor is not public
class MyObj() {
constructor(someValue) {
this.someProp = someValue;
}
init() {
return Service.getService().then(val => {
this.asyncProp = val;
return this;
});
}
}
function createMyObj(someValue) {
let x = new MyObj(someVal);
return x.init();
}
createMyObj(someVal).then(obj => {
// obj ready to use and fully initialized here
}).catch(err => {
// handle error here
});
If you're using modules, you can export only the factory function (no need to export the class itself) and thus enforce that the object is initialized properly and not used until that initialization is done.
Break async object initialization into a separate method that can return a promise
class MyObj() {
constructor(someValue) {
this.someProp = someValue;
}
init() {
return Service.getService().then(val => {
this.asyncProp = val;
});
}
}
let x = new MyObj(someVal);
x.init().then(() => {
// ready to use x here
}).catch(err => {
// handle error
});
Use Events to Signal Completion
This scheme is used in a lot of I/O related APIs. The general idea is that you return an object from the constructor, but the caller knows that object hasn't really completed its initialization until a particular event occurs.
// object inherits from EventEmitter
class MyObj extends EventEmitter () {
constructor(someValue) {
this.someProp = someValue;
Service.getService().then(val => {
this.asyncProp = val;
// signal to caller that object has finished initializing
this.emit('init', val);
});
}
}
let x = new MyObj(someVal);
x.on('init', () => {
// object is fully initialized now
}).on('error', () => {
// some error occurred
});
Hackish way to put the Async Operation in the Constructor
Though I wouldn't recommend using this technique, this is what it would take to put the async operation in the actual constructor itself:
class MyObj() {
constructor(someValue) {
this.someProp = someValue;
this.initPromise = Service.getService().then(val => {
this.asyncProp = val;
});
}
}
let x = new MyObj(someVal);
x.initPromise.then(() => {
// object ready to use now
}).catch(err => {
// error here
});
Note, you see the first design pattern in many places in various APIs. For example, for a socket connection in node.js, you would see this:
let socket = new net.Socket(...);
socket.connect(port, host, listenerCallback);
The socket is created in the first step, but then connected to something in the second step. And, then the same library has a factory function net.createConnection() which combines those two steps into one function (an illustration of the second design pattern above). The net module examples don't happen to use promises (very few nodejs original apis do), but they accomplish the same logic using callbacks and events.
Other note on your code
You likely also have an issue with the value of this in your code. A .then() handler does not naturally preserve the value of this from the surrounding environment if you pass it a regular function() {} reference. So, in this:
function Constructor(){
Service.getService().then(function(data){
this.arr = data.data.array;
return this.arr
})
}
The value of this when you try to do this.arr = data.data.array; is not going to be correct. The simplest way to fix that issue in ES6 is to use a fat arrow function instead:
function Constructor(){
Service.getService().then(data => {
this.arr = data.data.array;
return this.arr
});
}

How to access super class method inside of internal generator function?

Look at this classes, Base and Derived, it's just simple classes that has "name" as property:
class Base {
constructor(name) {
this.name = name;
}
printName() {
console.log("Base: " + this.name);
}
}
class Derieved extends Base {
constructor(name) {
super(name);
}
// Override
printName() {
// IIFE.
(function() {
super.printName(); // Can't use super here
})();
console.log("Derived: " + this.name);
}
}
var obj = new Derieved('John');
obj.printName();
I want to invoke Base::printName from Derieved::printName. But some reason, I have to invoke inside of internal function of Derieved::printName.
But run above code, it fails with:
SyntaxError: 'super' keyword unexpected here
If I saved the reference of parent's method to variable, looks like it can called but can't access any properties, it saids undefined.
TypeError: Cannot read property 'name' of undefined
I just wrote that internal function is just normal function, but actually it's generator function, so I can't use arrow function:
get(match: T, options: IQueryOptions|void): Promise<Array<Object>|Error> {
const superGet = super.get;
return new Promise((resolve, reject) => {
co(function*() {
try {
// I need to invoke parent's get method here!!
const accounts = yield superGet(match, options);
... // do something with accounts
resolve(accounts);
}
catch(err) {
...
}
});
});
}
Is there a way to to this? Why I can't save the reference of parent's method into variable?
super can be accessed only from child method, not from the scope of functions that are called inside this method.
Generator functions are still functions and support binding. This will result in bound function that cannot be identified as generator function by its signature, but as long as coroutine library supports generic iterators (co does), that's fine.
So basically it is
get(...) {
const superGet = super.get;
return new Promise((resolve, reject) => {
co(function*() {
...
const accounts = yield superGet.call(this, match, options);
...
}.bind(this));
});
}
Or even better:
get(...) {
const superGet = super.get.bind(this);
return new Promise((resolve, reject) => {
co(function*() {
...
const accounts = yield superGet(match, options);
...
});
});
}
There are several things that can be improved here. The first one is that it uses promise constructor antipattern. co already returns a promise, there's no need to wrap it with new Promise.
Another thing is that it is beneficial to separate generator method and promisified method, for the sake of seamless inheritance. co supports delegated yields, it makes this even easier:
class Base {
get(...args) {
return co(this._get.bind(this, ...args));
}
* _get(...) { ... }
}
class Child extends Base {
* _get(...) {
...
const accounts = yield* super._get(match, options);
...
}
}
Both TypeScript and Babel support ES2017 async..await and are are able to fall back to co-like generator coroutines in ES6 target output. This makes co efficiently useless in transpiled JS projects, the code above becomes:
class Child extends Base {
async get(...) {
...
const accounts = await super.get(match, options);
...
}
}
Apparently you employ some compiler for the type annotations anyway. In that case, the solution is to just drop the co library and generators, and use proper modern async/await syntax instead, which is supported by TypeScript, Babel and native nodejs:
async get(match: T, options: IQueryOptions|void): Promise<Array<Object>|Error> {
try {
const accounts = await super.get(match, options);
… // do something
return accounts;
}
catch(err) {
…
}
}
super will just work out of the box here.
Arrow functions to the rescue!
class Base {
constructor(name) {
this.name = name;
}
printName() {
console.log("Base: " + this.name);
}
}
class Derieved extends Base {
constructor(name) {
super(name);
}
// Override
printName() {
// IIFE
(() => {
super.printName(); // Can't use super here
})();
console.log("Derived: " + this.name);
}
}
var obj = new Derieved('John');
obj.printName();
basically, the arrow function maintains the this and super contexts, unlike the literal function keyword

Categories