1 of 13

Async initialization

Bradley Farias - GoDaddy

2 of 13

Async initialization

  • ws = new WebSocket(url);�await new Promise((f, r) => {� ws.onopen = f;� ws.onerror = r;�};
  • await Foo.create();
  • await new Remote();

3 of 13

Async initialization

  • Creating things via `new` and async using classes is… weird
  • class X {� a = 1;� constructor() {� return Promise.resolve().then(async () => {� await null;� return this;� });� }�}�class Y extends X {}�console.dir(new Y());

Promise => { a: 1 }

4 of 13

Async initialization

  • class Y extends X {� b = 2; // put on the Promise?�}�const y = await (new Y());�console.log(� y.a,� 'b' in y,� y instanceof Y�);�

1�false�true

5 of 13

Multiple potential workflows to let user handle async init

  • existing event handler methods
  • existing static helper methods
  • existing promise return pattern�
  • all allow constructor to return uninitialized instance
    • Could add guard value boilerplate to the workflow
    • Could make constructor unable to be called directly

6 of 13

Just want to simplify DX for async initialization

  • Async constructors OR Async classes
    • Lots less boilerplate, but may require new semantics / syntax
  • Private constructors
    • Would still need boilerplate, but allows at least hiding the constructor easily to avoid access to partial initialization
    • Solves other desires
  • Somehow make async fluent APIs require less boilerplate
    • Repeated `await this.#ready` boilerplate
    • Subclassing still is awkward
      • Fields still attach to Promise ?

7 of 13

Just want to simplify DX for async initialization

  • We are adding features like fields and would like to cycle back to ergonomics of language feature interactions.

8 of 13

Before we go on...

  • Subclass already doesn't have guarantee that `this` is an "instance" of superclass due to super override.
  • function NotDate() {� return new Date();�}class X extends NotDate {}�assert(new X() instanceof Date);
  • The new concept here is not just allowing super to manipulate what subclass sees as `this` but allowing subclass to opt into async coordination.

9 of 13

Async constructors

  • class X {� async constructor() {� await null;� }�}�class Y extends X {� async constructor() {� // bikeshed to have `this` be the instanceawait.super();� }�}�

10 of 13

Async classes?

  • async class X {� async constructor() {� await null;� }�}�async class Y extends X {� async constructor() {� super();� }�}�

11 of 13

Private constructors

  • class X {� #constructor() { }� static async create() {� // bikeshed on how to access the constructor� // class.this would have been niceconst ret = new X.prototype.#constructor();� await null;� return ret;� }�}�

12 of 13

Keep using Fluent APIs

  • class X {� ready = null;� constructor() {� this.ready = asyncFunction();� // can't return a Promise, need to keep `this`� }� async x() {� await this.ready;� }�}�

13 of 13

Keep using Fluent APIs

  • class Y extends X {� constructor() {� this.ready = this.ready.then(() => otherInit());� }� get y() {� // can't await :-/� }�}�