ES6 导入的定义执行顺序是什么?
What is the defined execution order of ES6 imports?
我尝试在互联网上搜索导入模块的执行顺序。例如,假设我有以下代码:
import "one"
import "two"
console.log("three");
其中one.js
和two.js
定义如下:
// one.js
console.log("one");
// two.js
console.log("two");
控制台输出是否保证为:
one
two
three
还是未定义?
JavaScript 模块被评估异步。但是,所有导入都在模块主体进行导入之前进行评估。这使得 JavaScript 模块不同于 Axel Rauschmayer Exploring ES6 的 CommonJS modules in Node or <script>
tags without the async
attribute. JavaScript modules are closer to the AMD spec when it comes to how they are loaded. For more detail, see section 16.6.1。
因此,在提问者提供的例子中,无法保证执行顺序。有两种可能的结果。我们可能会在控制台中看到:
one
two
three
或者我们可能会看到这个:
two
one
three
换句话说,两个导入的模块可以以任何顺序执行它们的 console.log()
调用;它们相对于 彼此 是异步的。但它们肯定会在导入它们的模块主体之前执行,因此 "three"
保证最后记录。
使用 top-level 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 更新
根据 ,从 ES2020 开始,非异步模块的评估顺序似乎得到保证。因此,如果您知道要导入的 none 个模块包含顶级 await
语句,它们将按照导入的顺序执行。在提问者的例子中,控制台输出总是:
one
two
three
根据最新规范InnerModuleEvaluation, the order of module.ExecuteModule()
is guaranteed since [[RequestedModules]] is an ordered list of source code occurrences。
// 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
我尝试在互联网上搜索导入模块的执行顺序。例如,假设我有以下代码:
import "one"
import "two"
console.log("three");
其中one.js
和two.js
定义如下:
// one.js
console.log("one");
// two.js
console.log("two");
控制台输出是否保证为:
one
two
three
还是未定义?
JavaScript 模块被评估异步。但是,所有导入都在模块主体进行导入之前进行评估。这使得 JavaScript 模块不同于 Axel Rauschmayer Exploring ES6 的 CommonJS modules in Node or <script>
tags without the async
attribute. JavaScript modules are closer to the AMD spec when it comes to how they are loaded. For more detail, see section 16.6.1。
因此,在提问者提供的例子中,无法保证执行顺序。有两种可能的结果。我们可能会在控制台中看到:
one
two
three
或者我们可能会看到这个:
two
one
three
换句话说,两个导入的模块可以以任何顺序执行它们的 console.log()
调用;它们相对于 彼此 是异步的。但它们肯定会在导入它们的模块主体之前执行,因此 "three"
保证最后记录。
使用 top-level 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 更新
根据 await
语句,它们将按照导入的顺序执行。在提问者的例子中,控制台输出总是:
one
two
three
根据最新规范InnerModuleEvaluation, the order of module.ExecuteModule()
is guaranteed since [[RequestedModules]] is an ordered list of source code occurrences。
// 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