等待动态导入

mth*_*ers 4 async-await typescript reactjs

我使用create-react-app创建了一个带有 React 和 Typescript 的 Web 应用程序。它使用了一个相当繁重的第三方库。我想通过使用动态导入表达式从主包中排除它。

所以,import { Component } from 'library'我没有做,而是创建了一个看起来像这样的小包装器:

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,
};
Run Code Online (Sandbox Code Playgroud)

然后,在我的应用程序中,我会使用import { Component } from '../library-wrapper.ts'. 因为包装器使用动态导入表达式,Webpack 创建了一个单独的块,只有浏览器才真正需要它。\o/.

但坏消息是:我的包装器实际上并不等待动态导入表达式。它调用loadLibrary()函数但立即继续执行,记录

typeof 组件未定义

因此,当我尝试使用 时Component,React 崩溃了:

元素类型无效:应为字符串(对于内置组件)或类/函数(对于复合组件)但得到:未定义。

有什么建议是怎么回事?

Frx*_*rem 8

loadLibrary是一个异步函数,所以它返回一个promise而不是一个普通对象。因此,您必须执行await loadLibrary()loadLibrary().then(...)获取库对象。

这样做的结果是您不能静态导出动态导入的内容,因为静态导入/导出是立即同步完成的,而动态导入是异步完成的。您只能导出函数loadLibrary,并让模块的用户在需要库时调用该函数。

简而言之,一次异步,永远异步;你不能强制异步的东西在 JavaScript 中同步运行。


另外,loadLibrary顺便说一句,您的功能可以简化很多,只需

const loadLibrary = () => import('library');
Run Code Online (Sandbox Code Playgroud)

因为 a).then((r) => r)只是创建了一个承诺的相同副本,b) 在异步函数中返回之前你不必等待(它是自动完成的)和 c) 无论如何都返回一个承诺的函数不必被标记为async(尽管如果您愿意,您仍然可以,例如为了可读性)。

  • 是的,你是对的,但你仍然需要使用 `await` 关键字来等待承诺(告诉 JS 你想要的承诺的结果而不是承诺本身)。而且因为你不能以同步方式运行异步代码(我认为这是由于 JavaScript 的单线程设计),你只能在标记为 `async` 的函数中使用 `await`,否则你将不得不使用 `.then(r => { /* 对结果做一些事情 */ })`。 (2认同)