48

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

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

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

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

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

控制台输出是否保证为:

one
two
three

还是未定义?

4

2 回答 2

36

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

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

one
two
three

或者我们可能会看到:

two
one
three

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

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

// 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 开始,似乎可以保证非异步模块的评估顺序。因此,如果您知道要导入的模块都不包含顶级await语句,它们将按照导入的顺序执行。在提问者的示例中,控制台输出将始终为:

one
two
three
于 2016-02-22T11:17:41.147 回答
2

根据最新规范InnerModuleEvaluationmodule.ExecuteModule()由于[[RequestedModules]] 是源代码出现的有序列表,因此保证了 的顺序。

// 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
于 2021-11-03T08:56:35.407 回答