ES6 导入的定义执行顺序是什么?

IT技术 javascript ecmascript-6 es6-modules es6-module-loader
2021-03-14 07:53:29

我试过在互联网上搜索导入module的执行顺序。例如,假设我有以下代码:

import "one"
import "two"
console.log("three");

其中one.jstwo.js定义如下:

// one.js
console.log("one");

// two.js
console.log("two");

控制台输出是否保证为:

one
two
three

或者它是未定义的?

2个回答

JavaScript module是异步评估的但是,所有导入都会在module主体进行导入之前进行评估。这使得 JavaScript module不同于Node 中的 CommonJS module没有该属性的<script>标签asyncJavaScript module在加载方式方面更接近于AMD 规范有关更多详细信息,请参阅Axel RauschmayerExploring ES6第 16.6.1 节

因此,在提问者提供的例子中,无法保证执行的顺序。有两种可能的结果。我们可能会在控制台中看到:

one
two
three

或者我们可能会看到这个:

two
one
three

换句话说,两个导入的module可以console.log()以任意顺序执行它们的调用;它们是异步相对于彼此但是它们肯定会在导入它们的module主体之前执行,因此"three"保证最后记录。

使用顶级await语句(现在在 Chrome 中实现)时可以观察到module的异步性例如,假设我们稍微修改提问者的例子:

// main.js
import './one.js';
import './two.js';
console.log('three');

// one.js
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('one');

// two.js
console.log('two');

当我们运行时main.js,我们会在控制台中看到以下内容(添加了时间戳以进行说明):

[0s] two
[1s] one
[1s] three

从 ES2020 开始更新

根据petamoriken 的回答,从ES2020 开始,非异步module的评估顺序似乎得到了保证。因此,如果您知道要导入的module中没有一个包含顶级await语句,它们将按照导入的顺序执行。在提问者的例子中,控制台输出将始终是:

one
two
three
关于“导入的 ES6 module是异步执行的。”的任何参考?
2021-04-22 07:53:29
据我所知,ES2015 module不是异步导入的,而是 - 它们如何加载完全取决于module加载器。
2021-05-04 07:53:29
@BenjaminGruenbaum 我只能引用我自己的回答:“有关更多详细信息,请参阅Axel Rauschmayer 的 Exploring ES6 的第 16.6.1 节。”
2021-05-06 07:53:29
@BenjaminGruenbaum 是的,这就是为什么我添加了“没有现代浏览器实现 ES6 module的警告。我不知道 Babel 等转译器在这方面是否遵循原始规范。” 我怀疑大多数这样的转译器确实如您所建议的那样同步导入。但我的回答是关于原始规范的。
2021-05-09 07:53:29
我实际上是那本书的评论者。我要说的一点是,决定它的不是 ES module规范——而是由加载module的规范来决定如何加载module。IIRC 规范仅要求在代码执行时加载所有module。
2021-05-13 07:53:29

根据最新的规范InnerModuleEvaluation, 的顺序是module.ExecuteModule()有保证的,因为[[RequestedModule]] 是源代码出现的有序列表

// 16.2.1.5.2.1 rough sketch
function InnerModuleEvaluation(module, stack, index) {

  // ...

  // 8
  module.[[PendingAsyncDependencies]] = 0;

  // ...

  // 11: resolve dependencies (source code occurrences order)
  for (required of module.[[RequestedModules]]) {
    let requiredModule = HostResolveImportedModule(module, required);
    // **recursive**
    InnerModuleEvaluation(requiredModule, stack, index);

    // ...

    if (requiredModule.[[AsyncEvaluation]]) {
      ++module.[[PendingAsyncDependencies]];
    }
  }

  // 12: execute
  if (module.[[PendingAsyncDependencies]] > 0 || module.[[HasTLA]]) {
    module.[[AsyncEvaluation]] = true;
    if (module.[[PendingAsyncDependencies]] === 0) {
      ExecuteAsyncModule(module);
    }
  } else {
    module.ExecuteModule();
  }

  // ...

}

控制台输出始终如下:

one
two
three
对于非异步module,从 ES020 开始,这是正确的。
2021-05-05 07:53:29