Skip to content

Instantly share code, notes, and snippets.

@goloroden
Last active June 22, 2023 02:10
Show Gist options
  • Save goloroden/c976971e5f42c859f64be3ad7fc6f4ed to your computer and use it in GitHub Desktop.
Save goloroden/c976971e5f42c859f64be3ad7fc6f4ed to your computer and use it in GitHub Desktop.
Async constructors for JavaScript
// In JavaScript, constructors can only be synchronous right now. This makes sense
// from the point of view that a constructor is a function that returns a newly
// initialized object.
//
// On the other hand, it would sometimes be very handy, if one could have async
// constructors, e.g. when a class represents a database connection, and it is
// desired to establish the connection when you create a new instance.
//
// I know that there have been discussions on this on StackOverflow & co., but
// the so-far mentioned counter arguments (such as: doesn't work as expected
// with new) didn't convince me. Actually, it works as expected (see below),
// as long as you use await – but this is true for any async function ;-)
//
// Could this be an idea for TC39?
'use strict';
const { promisify } = require('util');
const sleep = promisify(setTimeout);
class Example {
// This is what I would like to have.
// async constructor () {
// await sleep(1000);
// this.value = 23;
// }
// This is how it could be implemented.
constructor () {
return new Promise(async (resolve, reject) => {
try {
await sleep(1000);
this.value = 23;
} catch (ex) {
return reject(ex);
}
resolve(this);
});
}
}
(async () => {
// It works as expected, as long as you use the await keyword.
const example = await new Example();
console.log(example instanceof Example);
console.log(example.value);
})();
@Fenzland
Copy link

I'm trying to write code like this:

class Foo
{
    async constructor(){}
}

but not work. Then, I search and found your gist. Thank you for your idea.
Async constructor is necessary for async coding. In some case, a object cannot be construct syncly. Instead of getting a useless object and don't know when it'll work, receiving a promise is more helpful.

Here is my async constructor:

class Foo
{
    constructor(){
        return (async ()=> {
            this.foo= await somePromise;
            return this;
        })();
    }
}

@zanethomas
Copy link

zanethomas commented Apr 27, 2019

Here's something I've been playing with the past couple days

function Post(db, post) {
  if (!new.target) {
    return new Post(db, post);
  }
  this.db = db;
  this.post = post;
  //
  // create new post
  //
  return new Promise(async (resolve, reject) => {
    try {
      //
      // if we have an id then the post is already in the
      // database and we have already loaded it!
      //
      if (!post.id) {
        let id = await this._insert(post);
        this.post.id = id[0];
      }
      resolve(this);
    } catch (err) {
      reject(err);
    }
  });
}
  let post = await Posts.create({
    title: 'title',
    body: 'body',
    author_id: author.id
  });
Posts.create = async function(params) {
  return Post(state.db, params);
}

@Dimtime
Copy link

Dimtime commented Aug 26, 2019

I have same idea - we need it. I try to put that idea in standard, but i need more support...
My discuss of proposal here:
CVzP-DINO3s

@Dimtime
Copy link

Dimtime commented Aug 27, 2019

How it says: "there is no limit to perfection" - new wrapper
Now it works like more native:

class PromiseClass {
static async new(test='test'){ this.promise= test; return this;}
constructor(...args) {
let s = async()=>PromiseClass.new.call(this,...args);
return (async r=>await s() )(); 
}//new 
}//class 
class AsyncClass extends PromiseClass{
static async new(){ return this; }
constructor(...args){ 
let s = async()=>{ await super(); return AsyncClass.new.call(this,...args); };
return (async r=>await s() )(); 
}//new 
}//AsyncClass

@Dimtime
Copy link

Dimtime commented Aug 27, 2019

We continue discuss here.
All we need just right words i think. Seems async class or async constructor - too bad.
Maybe promise_class sounds more good...

@Dimtime
Copy link

Dimtime commented Feb 12, 2020

@Dimtime
Copy link

Dimtime commented Aug 2, 2020

My new version of wrapper and you can send your variants - tc39/proposal-async-init#7

class PromiseClass {
static async $new(){this.promise='promise'; return this;}
constructor(...args) {return this.constructor.$new.call(this,...args);}//new
}//class

class AsyncClass extends PromiseClass{
static async $new(){super.$new.call(this); this.async='async'; return this;}
constructor(...args){return super(...args)}//new
}//AsyncClass

@mannharleen
Copy link

Here's something I've been playing with the past couple days

function Post(db, post) {
  if (!new.target) {
    return new Post(db, post);
  }
  this.db = db;
  this.post = post;
  //
  // create new post
  //
  return new Promise(async (resolve, reject) => {
    try {
      //
      // if we have an id then the post is already in the
      // database and we have already loaded it!
      //
      if (!post.id) {
        let id = await this._insert(post);
        this.post.id = id[0];
      }
      resolve(this);
    } catch (err) {
      reject(err);
    }
  });
}
  let post = await Posts.create({
    title: 'title',
    body: 'body',
    author_id: author.id
  });
Posts.create = async function(params) {
  return Post(state.db, params);
}

good one!

@bakasura980
Copy link

bakasura980 commented Jan 6, 2022

There is one more way of doing an async constructor - Proxy

Typescript:

class A {

    private data: any
    private info: any

    public constructor (data: any, info: string) {
        this.data = data
        this.info = info
    }

}

interface InterfaceA {
    new(info: string): A;
}

class AProxy {

    public static init (): InterfaceA {
        const proxyHandler = {
            construct: async function (args: any) {
                // Some async operation
                const data = await retrieve()
                return new A(data, args)
            }
        }

        return new Proxy(A, proxyHandler)
    }

}

export default AProxy.init()

import A from './a.ts'

(async () => {
    const a = await new A('Some iNFO')
})()

Javascript:

class A {
    constructor (data, info) {
        this.data = data
        this.info = info
    }
}

class AProxy {

    static init () {
        const proxyHandler = {
            construct: async function (args) {
                // Some async operation
                const data = await retrieve()
                return new A(data, args)
            }
        }

        return new Proxy(A, proxyHandler)
    }

}

module.exports = AProxy.init()

But to be honest, if you need an async constructor, it is always better to use the build pattern as it is more readable and understandable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment