EcmaScript 如何处理递归模块?

问题描述 投票:0回答:1

最近我在项目中遇到了关于循环依赖的 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

我的问题是:

  1. 循环引用的工作应该有什么标准?为什么我的第一个示例有效而第二个示例无效?尽管它们都是循环依赖的。
  2. ES 模块如何处理这种递归情况?

我在 NodeJS 文档中看到了关于递归

require
的解释,但不确定这是否也适用于 ES 模块:https://nodejs.org/api/modules.html#modules_cycles。 ES 模块是否使用与此处描述的完全相同的机制?

javascript circular-dependency circular-reference esmodules
1个回答
0
投票

只要遵循代码逻辑即可:

在同步代码中,如果导入

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

© www.soinside.com 2019 - 2024. All rights reserved.