Async Class

# Dimitrian Nine (6 years ago)

I cant find: was or not that proposal...

But can we have Async Class with async constructor and async super?

//AsyncClass
async_class AsyncClass { // keyword async_class(example)
constructor() {
await something(); // now constructor async too
}}

//AsyncObj in some async method
let NewAsyncObj = await AsyncClass(); // create async obj

//Extends
async_class ExtAsyncClass extends AsyncClass{
constructor() {
await super(); // now we can await super too
await something();
}}
# Isiah Meadows (6 years ago)

Is an async factory function/method not sufficient to do this? Also, note that you can return promises out of the constructor (and I've done it before).

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Dimitrian Nine (6 years ago)

"Is an async factory function/method not sufficient to do this?"

Maybe, but i think it question similiar to "factory vs class": we can use Сlass now, but why not to extend it to Async then?

"Also, note that you can return promises out of the constructor (and I've done it before)"

Like callback? Maybe, but again it simillial to "await vs callback" then.

I just like Class,Async and Await patterns. And think:this some sugar can be useful for some people like me...

# T.J. Crowder (6 years ago)

On Fri, Feb 16, 2018 at 7:51 AM, Dimitrian Nine <dimtimeforever at gmail.com>

wrote:

"Also, note that you can return promises out of the constructor (and I've

done it before)"

Like callback?

I think he means literally returning a promise. If you return an object out of the constructor, that becomes the result of the new operation. So you can literally return a promise of an instance instead of an instance (fiddle):

class Example {
    constructor() {
        return new Promise(resolve => {
            setTimeout(() => {
                const instance = Object.create(Example.prototype);
                resolve(instance);
            }, 200);
        });
    }

    foo() {
        console.log("foo called");
    }
}

It gets ugly(ier) when you want to extend that. :-)

-- T.J. Crowder

# Isiah Meadows (6 years ago)

I think he means literally returning a promise.

That would be the correct assumption. ;-)

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Dimitrian Nine (6 years ago)

"It gets ugly(ier) when you want to extend that. :-) "

Ok, i get it, but yes i prefer some more sugar like as i suggested before.

async class Files{
constructor(path){
this.blob = await get_file(path);
}}

async class ExtFiles extends Files{
constructor(path){
await super(path);
this.path = path;
}}

[file1,file2,file3] = await Promise.all([
new ExtFiles(path1),
new ExtFiles(path2),
new ExtFiles(path3)
]);
# Naveen Chawla (6 years ago)

You can just call an async method from the constructor (without await) - and then let that async method handle all the asynchronicity.

Does this satisfy your use case?

I think async constructor would make more sense than async class. Calling new MyObjectAsync() would have to return a promise for the object, requiring await to actually get the object.

My only reservation is that it does require caution not to presume that new X() isn't actually an instance of X (but that await new X() is). Otherwise I don't mind it.

# Dimitrian Nine (6 years ago)

"You can just call an async method from the constructor (without await) - and then let that async method handle all the asynchronicity."

Not sure about it - how control? callback? I prefer async/await then.

"I think async constructor would make more sense than async class"

Maybe: i use it for class more for difference between class and async class was more visible. But yes in total idea about async constructor. Both variants work for me. What more useful - i dont sure...

# Naveen Chawla (6 years ago)

Like this:

class MyObject{
    constructor(){
        initializeAsync();
    }

    async initializeAsync(){
        await doSomething();
        await doSomethingElse();
        //etc.
    }
}

Of course if you wanted to await at the constructor call level (and await inside the constructor itself), e.g.

    const myObject = await new MyObject();

...then of course you would need the "async constructor" feature, like you've requested, but then new MyObject() would return a promise.

Personally I have no problem with this:

It suffers from the same bug-set than you would get if you converted a synchronous function to an asynchronous one (i.e. the need to await).

# T.J. Crowder (6 years ago)

On Sun, Feb 18, 2018 at 8:27 AM, Naveen Chawla <naveen.chwl at gmail.com>

wrote:

Like this:

class MyObject{
    constructor(){
        initializeAsync();
    }

    async initializeAsync(){
        await doSomething();
        await doSomethingElse();
        //etc.
    }
}

That constructor breaks one of the main rules of promises: Either handle the rejection, or propagate the promise.

-- T.J. Crowder

# Naveen Chawla (6 years ago)

There is no rule that says you must propagate every promise. initializeAsync doesn't return anything, so the constructor just kicks off the async process.

# Naveen Chawla (6 years ago)

In the example, promise rejection can be handled with a try+catch inside initializeAsync() itself. But we're deviating from the topic of "async constructor", from which this is separate

# Dimitrian Nine (6 years ago)

"new MyObject() would return a promise "

I have one idea about it, but not sure good or not...

async class and then newAsync

Maybe this way more good for someone...

# T.J. Crowder (6 years ago)

On Sun, Feb 18, 2018 at 9:07 AM, Naveen Chawla <naveen.chwl at gmail.com>

wrote:

There is no rule that says you must propagate every promise.

I never said there was. I said you must handle rejection, or propagate the promise. Or, yes, you can ensure that the promise is never rejected, but that's not what the code I was responding to did. It left the door wide open to unhandled rejections.

-- T.J. Crowder

# kdex (6 years ago)

Having the constructor return a Promise is generally considered bad practice, since it breaks new X instanceof X, which in general breaks inheritance.

So, for this idea to work, you really shouldn't mark constructor as async (since this might suggest it returning a Promise, which it mustn't); instead, you would have to make the constructor return this, then make the asynchronous initialization, and then execute the rest. In other words,

something like this:

async class X {
	constructor() {
		await this.read();
		await this.write();
	}
	async read() { … }
	async write() { … }
}
console.log(new X());

would transpile like so:

class X {
	constructor() {}
	async initialize() {
		await this.read();
		await this.write();
	}
	async read() { … }
	async write() { … }
}
(new X()).initialize().then(x => {
	console.log(x);
});

The next problem with this idea is that you can't statically decide what new something is about to construct. Since something might be an async class, the StatementList following the construction of something would always have to be wrapped in a callback.

Long story short, introducing this feature would slow down new/ Reflect.construct.

# Dimitrian Nine (6 years ago)

"Long story short, introducing this feature would slow down new/ Reflect.construct" "since this might suggest it returning a Promise, which it mustn't "

Can it help: if async class constructor will be return Promise(obj) and i suggest before use newAsync for that practice?

Slow... but how much?

# Naveen Chawla (6 years ago)

I think awaitNew MyObject() would fit the thrust of your idea more. This would allow new MyObject() to still be an instance of the object instead of a promise, even if the constructor is async. It would dispatch the async parts of the constructor to the background instead of awaiting it. e.g.:

class MyObject {
    async constructor(){
        //sync stuff
        await doAsyncStuff();
        //etc.
    }
}

const
    myObjectWithOnlySyncStuffCompleted = new MyObject(), //Gets a new
instance but with only the "sync stuff" completed. Async stuff gets
dispatched to the background
    myObjectWithAsyncStuffCompleted = awaitNew MyObject() //Gets a new
instance with everything (including async stuff) completed
;

However, I have no problem the 1st version (new MyObject() returning a promise if the constructor is async). I think it's more consistent with how functions work anyway. But awaitNew serves the same requirement while ensuring new MyObject() is always an instance of MyObject.

# Dimitrian Nine (6 years ago)

"I think awaitNew MyObject() would fit the thrust of your idea more"

For me more good my first version... async class = return Promise(obj) But i can agree with others decisions, if in the end i get: easy way to async new

Some people ask me for some more interesting sample... Maybe it can help - try create:

//some module
async class FilterImg{
constructor(path,filters){
this.img = await load(path);
if (filters) {await this.use_filters(filters);}
}}
async use_filters(filters){
await filter1(this.image);
await filter2(this.image);
}}//class
export default FilterImg;
//end module

void async finction(){
let FilterImg = (await import(url)).default;
async class ExtFilterImg extends FilterImg{
constructor(path,filters){
await super(path);
if (filters) {await this.use_extFilters(filters);}
}}//class

let extFilter_img = await new FilterImg(path,filters);
console.log(extFilter_img.src+' '+extFilter_img.filters);
}();
# Isiah Meadows (6 years ago)

I'm still struggling through this thread to see why a static factory method/function with a private constructor/class doesn't suffice for this, or in this case, just procedural functions? (That's what I normally do, even in ES5.)

// filter-img.js
export async function create(path) {
    const img = await load(path)
    await filter1(img)
    await filter2(img)
    return img
}

// ext-filter-img.js
import * as FilterImg from "./filter-img.js"

async function create(path) {
    const img = await FilterImg.create(path)
    await extFilter1(img)
    await extFilter2(img)
    return img
}


(async () => {
    let ExtFilterImg = await import("./ext-filter-img.js")
    let extFilterImg = await ExtFilterImg.create(path)
    // ...
})()

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Dimitrian Nine (6 years ago)

"why a static factory"

I answered earlier: <<Maybe, but i think it question similiar to "factory vs class">>

Yes, you can, but for me more easy to work with classes. And i just want to extend the functionality for work with async - that all.

# Isiah Meadows (6 years ago)

I just thought I'd politely point out here that JS isn't a purely OO language (it's even less so class-based OO - ES classes are about 90% sugar over prototype-based inheritance*). Also, C#, the language that first had this kind of syntax, also doesn't have this. Constructors should be seen as what allocates and instantiates the instance, not what loads and creates the instance, and nearly every OO language out there assumes the former model, that of allocation/instantiation. (C++ is one of the unusual ones that conflates the two with its RAII.)

* There's a few features ES classes offer that prototypes don't, with new.target being the biggest one. But in general, ES classes aren't like traditional classes - they can be easily modified, up to and including their inheritance chain (which is downright trivial). Also, duck typing is reified in the spec, with that being used instead of interfaces when subclassing. Contrast this with Python, which makes it very difficult to change inheritance chains at runtime, and Ruby, which has had bugs over invalid assumptions with this (Classes don't invalidate their method caches with initialize_copy).


Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com

# Dimitrian Nine (6 years ago)

"ES classes are about 90% sugar" "ES classes aren't like traditional classes"

Yes and i just want to more sugar for them - just simple.

# Dimitrian Nine (6 years ago)

"why a static factory"

And more notes: "return img" - this is cut version of my example, you get image - not obj .

And my sample was simple. I hope not so long left to class private var + decorators

And sample can be then more complex with it:

async class Class{
@some_decorator
static private method(){}
static var = some_var
}

And have not only one Extends

Class
ExtClass
ExtExtClass

Maybe i try to force events, but time never waits...

# Dimitrian Nine (6 years ago)
class Test{
constructor(){}
async init(){}
static async newA(){
let obj = new Test(); obj.init(); return obj
}}
let t = Test.newA();

class ExtTest extends Test(){
constructor(){
super(); // we cant init here
//now we must recreate new/init for ExtTest too
}}

Or with my proposal we can easily do all async operation in constructor

async class Test{
constructor(){
}}
async class ExtTest extends Test(){
constructor(){
await super();
}}

Definition for async class = that async create and return Promise(obj)

# Dimitrian Nine (6 years ago)

«Classes, introduced in ECMAScript 2015, are primarily syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax does not introduce a new object-oriented inheritance model to JavaScript» «Classes are in fact "special functions"»

class = function Create(); 

all i want:

async class = async function Create(); 
# Dimitrian Nine (5 years ago)

Ok so much time has passed. I have learned more js. And created some wrapper for my idea:

Class is just function constructor that return object. Async Class is async function constructor that returns promise object. Wrapper code:

class PromiseClass { //promisified class
static async new(obj){ return obj;}
constructor() {return PromiseClass.new(this); }//new
}//class
class AsyncClass extends PromiseClass{ // can ext Class|PromiseClass
static async new(obj){return await obj();}
constructor(){return AsyncClass.new(async()=>{await super(); return this});}//new
}//AsyncClass

And we work with Async Class like we work with functions. I dont see here some differents to use them. Code without wrapper be clean:

async class PromiseClass { //return Promise
constructor() {}//async()=>Promise object
}//class
async class AsyncClass extends PromiseClass{ // can ext Class|PromiseClass
constructor(){ await super();} //async()=>Promise object
}//AsyncClass
# Isiah Meadows (5 years ago)

Not a bad idea, but I'd strongly prefer the promise to be returned from new AsyncClass(...) itself instead. And down that vein, super with async classes should also implicitly await in its super call, setting this to the value itself. For sanity reasons, super should return a promise resolved with the set this, so you can still use it and await it in inner arrow functions (it's possible), but it should still do its own implicit awaiting, just so you can add properties to the right instance. (This will also come into play with private properties - you want those on the instance, not the promise.)

I do have a preference: async should decorate the constructor, not the class. It's not the class that's async, it's the constructor itself. You can still have sync methods elsewhere in the class, and this doesn't do anything to affect that, hence why I'd prefer an async constructor(...) { ... } instead of an async class.


Isiah Meadows contact at isiahmeadows.com, www.isiahmeadows.com

# Dimitrian Nine (5 years ago)

Not a bad idea, but I'd strongly prefer the promise to be returned from new AsyncClass(...) itself instead

This about wrapper? He is just example of how functional can work right now.

I do have a preference: async should decorate the constructor, not the class

How i say before: both variants fine for me. But how discussed here - not all think same. For me Class is just wrapper on constructor. And Async Class is same only for async constructor.

# Dimitrian Nine (5 years ago)

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
# Claude Pache (5 years ago)

Le 26 août 2019 à 17:11, Dimitrian Nine <dimtimeforever at gmail.com> a écrit :

Class is just function constructor that return object

Although in JS a class is represented by the same object as its constructor, I don’t think it is good to assimilate the concept of JS class and JS constructor. Indeed, a class consists not only of its constructor, but also of all its associated methods. This is what is suggested by the syntax:

class C { 
    /* definition of all methods, not only constructor */ 
}

From that perspective, I understand very well what would be an “async constructor”; but an “async class” doesn’t make much sense.

# kai zhu (5 years ago)

class-factories are good enough. as real-world example, google-puppeteer uses class-factories to promisify its constructors, which are easy-to-reason.

if the below Browser/Page classes were also "async", it would be difficult to debug their intent in real-world code -- are their instances supposed to act as promises or as data-objects? i imagine code-maintennance would be a nightmare.

// "await" puppeteer's Browser constructor
// https://github.com/GoogleChrome/puppeteer/blob/v1.19.0/lib/Browser.js#L23
class Browser extends EventEmitter {
    constructor(connection, contextIds, ...) {
        ...
    }
    static async create(connection, contextIds, ...) {
        const browser = new Browser(connection, contextIds, ...);
        await connection.send(...);
        return browser;
    }
}
//
https://github.com/GoogleChrome/puppeteer/blob/v1.19.0/lib/Launcher.js#L184
const browser = await Browser.create(connection, [], ...);



// "await" puppeteer's Page constructor
// https://github.com/GoogleChrome/puppeteer/blob/v1.19.0/lib/Page.js#L36
class Page extends EventEmitter {
    constructor(client, target, ...) {
        ...
    }
    static async create(client, target, ...) {
        const page = new Page(client, target, ...);
        await page._initialize();
        ...
        return page;
    }
}
//
https://github.com/kaizhu256/puppeteer-to-istanbul-example/blob/6b3f599f/screenshot.js#L317
page1 = await module.exports.Page.create(null, tmp);
# Dimitrian Nine (5 years ago)

Claude Pache

class consists not only of its constructor

How i see it - seems all think that my idea not bad, but we have semantic hell. My thougth was Class is wrapper for constructor Object. And Async Class is same wrapper for constructor Promise of Object

kai zhu

class-factories are good enough

For all? Not for me - i want more clean and native methods.

if the below Browser/Page classes were also "async"

And way you described - try to Extends Browser Class. How you do that? I think my wrapper more easy and simple. .......................................................................

Sorry i am not philosopher and how it be named not strong interesting me. I need functionality and how i saw not only for me. just maybe start think about how it resolved? Maybe use another name - not async, but promise_class for example? Class => Obj, PromiseClass => Promise of Obj

# Dimitrian Nine (4 years ago)