4
\$\begingroup\$

I am experimenting with the best way to standardise my dynamic import() expressions when importing javascript modules.

import() returns a promise, so, to handle the asynchronous steps after theimport() expression, I can use either:

  • .then() syntax; or
  • async / await syntax

Whilst I am quite familiar with the older XHR2 approach and somewhat familiar with ES2015 .then() syntax, I am less familiar with async / await syntax.

I understand that I can only use await inside a function which has been declared or assigned with async.

But I am trying to understand why Approach 1 works.

Approach 1:

(async () => {

  let myImportedModule = await import('/path/to/my-module.js');

  myImportedModule.myFunction1();
  myImportedModule.myFunction2();

})();

But Approach 2 does not.

Approach 2:

(async () => {

  await import('/path/to/my-module.js');

  myFunction1();
  myFunction2();

})();

Additional Notes:

Clearly I have something wrong, but I thought Approach 2 would be the async / await equivalent of this snippet (using .then() syntax):

import('/path/to/my-module.js')

  .then((myImportedModule) => {

    myImportedModule.myFunction1();
    myImportedModule.myFunction2();

  });

but that it would enable me to access myFunction1 and myFunction2 directly, without needing to give a name to the resolved Promise.

\$\endgroup\$
  • \$\begingroup\$ It's the opposite. Approach 1 is the equivalent of the then version. Otherwise the meaning would be completely different. To understand this, think about how names are resolved and how they can be shadowed. \$\endgroup\$ – Aluan Haddad Feb 24 at 17:18
  • 1
    \$\begingroup\$ But that's not how modules work in general. Maybe you want a destructuring assignment (const {f, g} = await import(...);)? If you want them at module scope, that's highly problematic. \$\endgroup\$ – Aluan Haddad Feb 24 at 21:20
  • 1
    \$\begingroup\$ You may also be interested in github.com/tc39/proposal-top-level-await, which you can start using now if you use TypeScript or Babel. \$\endgroup\$ – Aluan Haddad Feb 24 at 21:25
  • 1
    \$\begingroup\$ Thanks for all your help, @AluanHaddad. This is a key statement: "But that's not how modules work in general." I've reconciled myself to importing an object with properties, rather than individual variables whenever I use dynamic import(). \$\endgroup\$ – Rounin Feb 24 at 23:37
  • 1
    \$\begingroup\$ Glad to be of service. I think the ergonomics don't bother me as much since I usually just export default expression, a habit I got into because of loader-interop issues and then became enamored of. I fully understand why many prefer to export multiple bindings though. \$\endgroup\$ – Aluan Haddad Feb 25 at 0:45
4
\$\begingroup\$

Approach 2 does not work because accoring to MDN:

import '/modules/my-module.js';

Is how you import a module for Import a module for its side effects only

Import an entire module for side effects only, without importing anything. This runs the module's global code, but doesn't actually import any values.

So, await import('/path/to/my-module.js'); won't actually import your myfunction1 and myFunction2 for you to use them, however, if you put an IIFE in there, it will be called.

If you don't want to use long names to call your function, you can destructure them :

(async () => {

  const { myFunction1, myFunction2 } = await import('/path/to/my-module.js');

  myFunction1();
  myFunction2();

})();

Or, call them in my-module.js and just import it and they will get called.

See the answers here for es6 import for side effects meaning.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Well explained answer. Thank you. \$\endgroup\$ – Rounin Feb 24 at 23:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.