Модули JavaScript (также известные как модули ES6) всегда выполняются в одном и том же предопределенном порядке.

Когда модуль импортирует другие модули, порядок, в котором выполняются все модули, гарантированно будет одинаковым. Порядок называется обход пост-заказа слева направо. Перед выполнением модуля (родительского) выполняются все импортируемые им модули (дочерние модули). Дочерние модули выполняются в том порядке, в котором они указаны в операторах import родительского модуля. Давайте проведем несколько экспериментов, чтобы продемонстрировать это.

Полный код трех тестов можно скачать с Github https://github.com/marianc000/executionOrderModules.

Порядок выполнения дочерних модулей

Для первой демонстрации я создаю восемь идентичных дочерних модулей a, b, c, d, e, f, g, h. Каждый из них содержит только одну строку кода - вызов глобальной функции recordExecution с URL-адресом модуля в качестве аргумента (модули могут получать свои URL-адреса из объекта import.meta, зависящего от модуля).

recordExecution(import.meta.url);

Вспомогательная функция recordExecution извлекает имя модуля из URL-адреса и добавляет его в конец глобального массива exections. recordExecution служит для записи выполнения модуля. После выполнения всех модулей массив exections будет содержать порядок их выполнения.

const re = /\/([a-z])\.js$/;
const exections = [];
function recordExecution(url) {
    // http://127.0.0.1:5500/Execution/js/a.js => a
    exections.push(re.exec(url)[1]);
}

Родительский модуль i импортирует восемь дочерних модулей и проверяет историю.

import   './a.js';
import   './b.js';
import   './c.js';
import   './d.js';
import   './e.js';
import   './f.js';
import   './g.js';
import   './h.js';
recordExecution(import.meta.url);
const order=exections.join('');
console.assert(order==='abcdefghi',order);

Для выполнения модулей в браузере я использую тег script:

<script src="js/i.js" type="module"></script>

В консоли браузера вы можете видеть, что девять примеров модулей всегда выполняются в порядке abcdefghi, сколько бы вы ни перезагружали страницу. Сначала восемь импортированных модулей выполняются в порядке, указанном операторами import с их именами. Родительский модуль i выполняется последним.

Пост-заказ обход

Теперь давайте более тщательно проверим, следует ли порядок выполнения модуля JavaScript обходу пост-порядка слева направо. Я добавил import операторов к восьми модулям выше, чтобы воспроизвести дерево зависимостей, показанное на иллюстрации вверху.

Теперь модуль f является корневым-родительским для всех оставшихся модулей в дереве зависимостей. Он будет запущен из браузера.

import   './b.js';
import   './g.js';
recordExecution(import.meta.url);
const order=exections.join('');
console.assert(order==='acedbhigf',order);

модуль b:

import   './a.js';
import   './d.js';
recordExecution(import.meta.url);

модуль g:

import   './i.js';
recordExecution(import.meta.url);

модуль d:

import   './c.js';
import   './e.js';
recordExecution(import.meta.url);

модуль i:

import   './h.js';
recordExecution(import.meta.url);

Конечные модули a, c, e, h ничего не импортируют и остаются такими же, как в предыдущем контрольная работа.

Девять модулей действительно выполняются в порядке acedbhigf, показанном на иллюстрации прохождение пост-порядка слева направо.

Влияние верхнего уровня await на порядок выполнения модуля

Без await модули ES6 гарантированно работают в том же детерминированном порядке. Чтобы увидеть, как await изменяет предопределенный порядок, я вставил await в модуль d, который одновременно является родительским и дочерним:

import './c.js';
import './e.js';
//blocks the module execution for 1 second
await new Promise(resolve => setTimeout(resolve, 1000));
recordExecution(import.meta.url);

Поскольку await блокирует выполнение d и его родительского элемента b на одну секунду (время, достаточное для выполнения оставшихся крошечных модулей), порядок должен измениться на acehigdbf.

Как и в предыдущем тесте, я проверяю порядок в корневом модуле f:

import   './b.js';
import   './g.js';
recordExecution(import.meta.url);
const order=exections.join('');
console.assert(order==='acehigdbf',order);

Порядок действительно всегда acehigdbf.