我两天前看了BUN的文件.我很难理解这个:

"The biggest difference between CommonJS and ES Modules is that CommonJS modules are synchronous, while ES Modules are asynchronous. ".

我在谷歌上搜索了一下,发现很多人也提到了这一点,但没有人解释.以下是一些参考:

在我看来,ES模块的加载与静态导入是同步的.例如:

// exporter.mjs
console.log("exporter");
// importer.mjs
import "exporter.mjs"
console.log("importer");

使用此命令node importer.mjs.如果exporter.js的加载是异步的,则输出将首先是"Importer"打印,然后是"Exporter"打印.而结果是"出口商"首先打印,"进口商"紧随其后. enter image description here

这证明了ES模块的加载与静态导入是同步的,在这种情况下,我们可以在CommonJS模块中导入ES模块.虽然我上面提到的文章都说"我们不能在CommonJS模块中导入ES模块,但我们必须使用the dynamic import function",NodeJS的文档也是如此.但是,我们可以在BUN的CommonJS模块中导入ES模块.这是一个例子:

// exporter.mjs
export let number = 10;
// importer.cjs
const { number } = require("./exporter.mjs");

console.log(number);

使用此命令bun importer.cjs.我们可以像这样得到输出. enter image description here

我真的很困惑.

推荐答案

"CommonJS模块是同步的,而ES模块是异步的"这种 idea 本身可能有点过于简单化,从而导致了这种混乱.让我们添加一些上下文.

How do ES Modules work?

ES模块经历以下步骤:

  1. Load:异步加载依赖项.

    从规范中:

    通过递归加载模块的所有依赖项来准备模块进行链接,并返回promise .

  2. Link:将模块导出与内存中的导入相连接.

    从规范中:

    通过可传递地解析所有模块依赖项并创建模块环境记录来准备要判断的模块.

    在调用此方法之前,LoadRequestedModules必须已成功完成.

  3. Evaluate (and execute):运行代码.

    从规范中:

    返回此模块及其依赖项的求值promise ,在求值成功或已成功求值时进行解析,并因求值错误或求值失败而拒绝.如果promise 被拒绝,主办方将处理promise 拒绝并重新抛出判断错误.

    在调用此方法之前,链接必须已成功完成.

正如您从JavaScript/ECMAScript spec中的引号中看到的,不同的步骤是异步执行的,但是步骤之间相互依赖.实际上,这意味着依赖项将在依赖项之前判断和执行,这给人一种ES模块是同步的印象.

How do CommonJS modules work?

CommonJS不同,因为它使用函数(require)来导入模块.这意味着,只有在运行时(代码执行时)才知道依赖项.换句话说,首先执行代码,然后加载依赖项;与ES模块相反,ES模块首先加载依赖项,然后执行代码.require的同步特性是因为当遇到require函数时,函数之后的代码直到require函数加载并执行依赖项内的代码之后才运行.

A practical comparison

在这样的情况下,加载顺序与执行顺序的差异变得很明显:

CommonJS ES Modules
a.js
const b = require("./b");

console.log(b);

exports.a = "a";
import { b } from "./b";

console.log(b);

export const a = "a";
b.js
const a = require("./a");

setTimeout(() => { console.log(a); }, 0);

exports.b = "b";
import { a } from "./a";

setTimeout(() => { console.log(a); }, 0);

export const b = "b";
Logs
b
undefined
b
a

在CommonJS的情况下,a.js需要b.js.因为require是同步的,所以它会阻止a的导出.因此,ab.js中没有定义.

在ES模块的情况下,虽然a.js也需要b.js,但是在执行代码之前设置aa.js中导出和在b.js中导入之间的链接.由于模块求值和执行是异步发生的,因此asetTimeout‘S回调执行之前被导出,这意味着a的值将被初始化为"a".之所以会发生这种情况,是因为promise 是在微任务队列中调度的,而定时器是在宏任务队列中调度的;并且微任务先于宏任务执行.这本身就是一个完整的话题,在互联网上的其他地方已经有了答案.我将在下面的进一步阅读中包括一些内容.

Further reading

Javascript相关问答推荐

v-textfield的规则找不到数据中声明的元素

使用ReactJS对Ant Design表中的空值进行排序

如何在RTK上设置轮询,每24小时

JQuery. show()工作,但. hide()不工作

微软Edge编辑和重新发送未显示""

如何修复我的js构建表每当我添加一个额外的列作为它的第一列?

康威的生活游戏规则在我的Javascript版本中不起作用.''我做错了什么?

在观察框架中搜索CSV数据

引用在HTMLAttributes<;HTMLDivElement>;中不可用

如何在每次单击按钮时重新加载HighChart/设置HighChart动画?

如何在ASP.NET项目中使用Google Chart API JavaScript将二次轴线值格式化为百分比

TypeError:无法分解';React2.useContext(...)';的属性';basename';,因为它为空

当输入字段无效时,我的应用程序不会返回错误

如何在 Select 文本时停止Click事件?

从另一个数组中的对应行/键值对更新数组中的键值对对象

是否可以在Photoshop CC中zoom 路径项?

在不删除代码的情况下禁用Java弹出功能WordPress

一个实体一刀VS每个实体多刀S

自定义图表工具提示以仅显示Y值

如何使用<;Link>;执行与JS Reaction中的";window.Location=/GameOverDied;";类似的操作?