简体   繁体   中英

Using async js imports in exported classes

Given a package that exposes async/dynamic exports. Which I currently import this way (but I could import it differently):

(async function() {
  const libEd = await import("../../.cache/ed25519wars/index.js");
})();

I intend to re-expose some functions from libEd as part of a class:

export class Something {
  static from_X() {
    return libEd.genFromX();
  }

  do_Y() {
    return libEd.doY();
  }
}

How can I do this ?


For more info:

  • The package that exposes async/dynamic exports is generated by webpack packing webassembly. I'm not sure if I can change anything about this part
  • I could definitely change the way I import that package
  • I could also change the way I re-expose / group the functions (and use something else than a class)

There's a couple of ways I would approach this:

  1. If the class doesn't need to be instantiated immediately, then I would await the library loading, then pass it to the class constructor. This is the cleanest way as the library is always defined within the class.

  2. If the class must be instantiated before fetching the library, then methods in the class must handle the situation when it is not defined (eg not loaded yet). You can then call something like await myClassInstance.init() to fetch the library. I typically provide a fallback for each method if the library is not loaded yet, perhaps it returns an empty string or a dummy UI.

EDIT: adding TypeScript example for option 1

interface MyLibrary {
  libraryMethod: () => void;
}

class ExampleClass {
  localLib: MyLibrary;

  constructor(lib: MyLibrary) {
    this.localLib = lib;
  }

  myClassMethod() {
    this.localLib.libraryMethod();
  }
}

async function run() {
  // first we fetch the remote library
  const myLibrary: MyLibrary | undefined = await import('/lib.js');

  // good practise to add some check here
  if (!myLibrary) {
    throw Error('failed to fetch myLib');
  }

  // then we create the class instance using the downloaded library
  const myClassInstance = new ExampleClass(myLibrary);

  // now we can call our class method which definitely has the remote library
  myClassInstance.myClassMethod();
}

I ended up settling on either of three methods:

  1. @Tim's method (accepted answer): include the import in the class properties, and await at constructor time.

    But: there might be overhead associated with storing the import in each instance.


  1. await ing in each method of the class to have the import defined there:
export class Something {
  static async from_X() {
    const libEd = await loadLibProm();
    return libEd.genFromX();
  }

  async do_Y() {
    const libEd = await loadLibProm();
    return libEd.doY();
  }
}

But: the class API is now all async and more awkward to use.


  1. Pre-loading the import at code start. And hoping the load is going to be finished when the class is called.
let libEd: typeof import("../../.cache/ed25519wars/index.js");
async function loadLibProm(): Promise<
  typeof import("../../.cache/ed25519wars/index.js")
> {
  libEd = await import("../../.cache/ed25519wars/index.js");
  return libEd;
}
loadLibProm(); // this somehow starts running the promise ?? wut ? Anyways that's what we want

export class Something {
  static from_X() {
    return libEd.genFromX();
  }

  do_Y() {
    return libEd.doY();
  }
}

But: this needs better error handling for the case an instance of the class is created before the import is finished / after the import failed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM