4

As showed here (or here) we can use external class definition in many browsers and NodeJs... But the most useful way to load the external class is

import('./MyClass.js').then(({default: MyClass}) => {
   let x = new MyClass(); // using here!
   // ... but it is not global, is AN ISLAND IN A BLOCK
 }); // async loading 

... but it is not global, is an island in a async block. So, how to do it globally?

TESTING GLOBAL ALTERNATIVES AND ERRORS:

 const MyClass = () => import('/MyClass.js'); // ok, but...
 let x = new MyClass()
 // Uncaught TypeError: MyClass is not a constructor

 const MyClass = await import('/MyClass.js');  
 // Uncaught SyntaxError: await is only valid in async function

The module = await import(moduleFile) form is suggested here.


For "global class" suppose an external Javascript file MyClass.js like this:

export default class MyClass {
  constructor(x) {
    this.val=x? x: "Hello!"
    console.log("MyClass ok...")
  }
}
Peter Krauss
  • 13,174
  • 24
  • 167
  • 304

1 Answers1

7

Generally, you don't want to do things globally when you're using modules. That's part of the point of modules.

If you're importing dynamically, then by the nature of what you're doing it's going to be an asynchronous process, which means having code that waits for it to complete (for instance, a then handler or using await in an async function).

You could write to a global in the then handler (or after await in an async function), but it's normally a bad idea, since there will be a period of time when the global doesn't have the value (yet).

// **NOT** RECOMMENDED
import("/MyClass.js")
.then(cls => {
    window.MyClass = cls; // Or `global.MyClass = cls;` on Node.js
})
.catch(error => {
    // Handle error
});

Or to a module global:

// **NOT** RECOMMENDED
let MyClass;
import("/MyClass.js")
.then(ns => {
    MyClass = ns.default;
})
.catch(error => {
    // Handle error
});

(Note that what you receive from dynamic import is the module namespace object. In your case you're using the default export, which is accessible via the default property on the MNO.)

In both cases, though, code may try to use it before it's been filled in. More: How do I return the response from an asynchronous call?

Instead, basically, put all of the code that needs that class in a then handler, or in an async function after await. Live Example

(async () => {
    const {default: MyClass} = await import("./MyClass.js");
    let c = new MyClass();
    // ...
})()
.catch(error => {
    // Handle/report error
    console.error(error);
});

(Notice the destructuring to get MyClass from the default on the MNO.)

See also: How can I use async/await at the top level?

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Now it is a TypeError (Firefox and Chorme), same problem with the simplest form `(async () => { console.log(1) })().catch(error => { console.log(2,error) });` – Peter Krauss Mar 05 '19 at 18:55
  • @PeterKrauss - I forgot that dynamic `import` provides a module namespace object. See the latest edit (and [live example](https://embed.plnkr.co/mf1GIEmNYdtcu79EWbqN/)). – T.J. Crowder Mar 05 '19 at 19:05
  • Thanks Crowder, now is running with Chrome v72, but only it... Firefox v65.0 say *"SyntaxError: dynamic module import is not implemented"*... Seens that we must wait 2020 to use in main browsers. – Peter Krauss Mar 05 '19 at 22:04
  • 1
    @PeterKrauss - Probably not that long, but yes, [dynamic import](https://github.com/tc39/proposal-dynamic-import) is still only a proposal, though it's at an advanced stage (stage 3 of [the process](https://tc39.github.io/process-document/)), which means it's getting implementation in browsers, but remember browsers only just got static import support. In the meantime, you can use tools like [webpack](https://webpack.js.org/) and [rollup.js](https://rollupjs.org/guide/en) that bundle things together, handling import and export. – T.J. Crowder Mar 06 '19 at 08:25