最近我在项目中遇到了关于循环依赖的 eslint 警告。我以前认为在 ES 模块中循环引用是不可能的。但显然它们在某些情况下起作用。
我尝试重现循环依赖的最小工作示例。
这有效:
// module-a.mjs
import { b } from "./mb.mjs";
export const a = 1;
export function fa() {
console.log(b);
}
setTimeout(() => {
fa();
});
// module-b.mjs
import { a } from "./ma.mjs";
export const b = 2;
export function fb() {
console.log(a);
}
setTimeout(() => {
fb();
});
但是上面没有
setTimeouts
的相同代码会抛出错误,或者为了更简单,下面的代码不起作用:
// module-a.mjs
import { b } from "./mb.mjs";
export const a = 1;
console.log(b);
// module-b.mjs
import { a } from "./ma.mjs";
export const b = 2;
console.log(a);
错误将是:
Uncaught ReferenceError: Cannot access 'a' before initialization
我的问题是:
我在 NodeJS 文档中看到了关于递归
require
的解释,但不确定这是否也适用于 ES 模块:https://nodejs.org/api/modules.html#modules_cycles。 ES 模块是否使用与此处描述的完全相同的机制?
只要遵循代码逻辑即可:
在同步代码中,如果导入
ma.mjs
,它所做的第一件事就是导入 mb.mjs
,它会初始化 b
,然后尝试打印尚未设置的 a
。
在超时情况下,如果导入
ma.mjs
,它首先导入 mb.mjs
(与之前相同),设置 b
,并将要打印 a
的任务放入任务队列中。然后它继续执行 ma.mjs
,这会设置 a
,并将要打印 b
的任务放入任务队列中。这样就完成了初始任务,并且取出排队的任务来运行;但此时a
和b
都已定义,所以没有问题
即最好将此视为时间问题,而不是循环导入问题。实际上,您的示例相当于:
console.log(a); // error
const a = 3;
对
setTimeout(() => console.log(a)) // no error
const a = 3