Awaiting a dynamic import

13,226

loadLibrary is an asynchronous function, so it returns a promise instead of a regular object. Therefore you have to do await loadLibrary() or loadLibrary().then(...) to get the library object.

A consequence of this is that you can't statically export something that's imported dynamically, because static imports/export are done immediately and synchronously while dynamic imports are done asynchronously. You can only export the function loadLibrary, and let users of the module call that when they need the library.

In short, once asynchronous, always asynchronous; you can't force something asynchronous to run synchronously in JavaScript.


Also, as an aside, your loadLibrary function could be simplied a lot, to just

const loadLibrary = () => import('library');

since a) .then((r) => r) just creates an identical copy of a promise, b) you don't have to await before returning in an async function (it's done automatically) and c) a function that returns a promise anyways doesn't have to be marked as async (although you still can if you wish, for readability for example).

Share:
13,226
mthmulders
Author by

mthmulders

Enthusiastic software developer with a passion for elegant solutions. Eager to learn new things, willing to help others do the same.

Updated on July 20, 2022

Comments

  • mthmulders
    mthmulders almost 2 years

    I've created a web application with React and Typescript, using create-react-app. It uses a rather heavy third party library. I want to exclude that from the main bundle by using dynamic import expressions.

    So, instead of doing import { Component } from 'library', I've created a little wrapper that looks like this:

    const loadLibrary = async () => {
        const x = await import('library').then((r) => r);
        return x;
    };
    const {
        Component,
    } = loadLibrary() as any;
    
    // tslint:disable-next-line:no-console
    console.log(`typeof Component is ${typeof Component}`);
    
    export {
        Component,
    };
    

    And then, in my app, I'd use import { Component } from '../library-wrapper.ts'. Because the wrapper uses dynamic import expressions, Webpack creates a separate chunk which is only downloaded by the browser then it is actually needed. \o/.

    But the bad news is: my wrapper doesn't actually await the dynamic import expression. It invokes the loadLibrary() function but immediately continues execution, logging

    typeof Component is undefined

    So when I attempt to use the Component, React crashes:

    Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.

    Any suggestions what is going on?

  • mthmulders
    mthmulders about 6 years
    I thought the whole idea of async/await is that you could actually wait for a Promise to be resolved. Have I been wrong in this case?
  • Frxstrem
    Frxstrem about 6 years
    Yes, you're right, but you still have to use the await keyword to wait for a promise (to tell JS that it's the result of a promise you want instead of the promise itself). And because you can't run asynchronous code in a synchronous way (which I believe is due to the single-treaded design of JavaScript), you can only use await inside a function marked as async, otherwise you would have to use .then(r => { /* do something with result */ }).