我正在尝试动态导入ES6模块(Chrome 109在Windows 10上,重新测试与Firefox 108)。我想使用顶级await
来等待模块可用。
这是一个展示main.js,它是由<script type="module" src="main.js"></script>
加载。我将import和await分开,这样我就可以用console.log转储承诺。
let modulePromise = import("./test_dummy.js");
setTimeout(() => console.log("timeout", modulePromise), 5000);
let module = await modulePromise;
module.test();
test_dummy.js如下所示:
// import { Foo } from "./foo.js";
console.log("Here is your test dummy");
export function test() {
console.log("Hello StackOverflow");
}
只要foo导入在注释中,就可以正常工作。但是只要我取消注释,模块就会被加载(我在DevTools Network选项卡中看到这一点),但不会执行(第一个console.log没有显示),并且承诺保持待定(我的setTimeout处理程序这样说)。不,它不是简单地等待慢速网络,我正在从本地主机加载这个。
问题是顶层的await,因为当我用.then()回调替换它时,模块加载。我尝试了一些复杂的变通方法,将导入封装在一个异步函数中。我甚至把它藏在另一个承诺里。没有运气。
尝试在顶层等待一个直接或间接依赖于后续导入的承诺似乎是有害的。为什么会这样?一开始我以为我发现的是Chrome浏览器的bug,但火狐浏览器却表现出同样的行为。
罗尔夫问候,编辑:我会尽量澄清的。test_dummy.js和foo.js被加载,我在DevTools的network选项卡中看到了这一点。但是它们没有被执行(第一个console.log没有显示)。foo.js的内容并不重要(当然,除了它应该是无错误的)。为了进行测试,我使用了一个虚拟模块,其中只有console.log。
如源代码所示,test_dummy.js模块导出了一个名为test的函数,而不是promise。我正在等待的承诺是模块承诺,创建了我的import()。当我等待它时,我得到一个Module对象,其中包含导出的数据。我尝试了一些不同的东西:
run_module("./test_dummy.js");
async function run_module(moduleName) {
(await import(moduleName)).test();
}
但是:run_module()
应该返回一个承诺,我应该能够在顶层等待它:
await run_module("./test_dummy.js");
...
但是当我这样做的时候,一切又停止工作了。正如我所说的:试图在顶层等待一个直接或间接依赖于在动态导入中触发的导入的承诺,似乎是有害的。
在run_module中使用我所有的代码是我现在的解决方案。但是我想知道为什么顶层的await会破坏一切。
看了Bergis的评论,我想我应该把这个作为一个(部分的)回答。
是的,我有循环引用。是的,我会穿粗麻布。谁能把骨灰倒在我头上?
main.js动态导入。/test_dummy.js, test_dummy.js静态导入。/foo.js,我没有提到foo.js从。/main.js静态导入一个helper对象。我认为这是无关紧要的,因为main.js已经加载,应该从内存导入。没有可见的网络请求
当我开始项目时,所有的导入都是静态的,并且循环引用不是问题。
当我删除反向引用并将我的testhelper对象作为参数注入test()函数调用时,顶层的await按预期工作。
但它仍然是一个轻微的痛点。为什么反向导入会杀死顶级等待?为什么异步函数的解决方案在工作?