Загрузить и выполнить порядок скриптов

Существует так много разных способов включить JavaScript в html-страницу. Я знаю о следующих вариантах:

  • встроенный код или загруженный из внешнего URI
  • включенный в тег head > или [1, 2]
  • не имеющий атрибута defer или async (только внешние скрипты)
  • включается в статический источник или динамически добавляется другими скриптами (в разных состояниях разбора с различными методами)

Не считая браузеров из жесткого диска, javascript: URI и onEvent -attributes [3], есть уже 16 альтернатив для получения JS и я уверен, что что-то забыл.

Я не очень заинтересован в быстрой (параллельной) загрузке, мне больше интересно узнать о порядке выполнения (что может зависеть от порядка загрузки и порядок документа). Есть ли хорошая (кросс-браузерная) ссылка , которая действительно охватывает все случаи?. http://www.websiteoptimization.com/speed/tweak/defer/ использует только 6 из них и тестирует в основном старые браузеры.

Как я боюсь, нет, вот мой конкретный вопрос: у меня есть (внешние) скрипты для инициализации и script загрузки. Затем у меня есть два статических встроенных сценария в конце тела. Первый позволяет загрузчику script динамически присоединить к телу другой элемент script (ссылающийся на внешние js). Второй из статических встроенных скриптов хочет использовать js из добавленного внешнего script. Может ли он полагаться на другой, который был выполнен (и почему: -)?

Ответ 1

Если вы не загружаете скрипты динамически или не помечаете их как defer или async, тогда скрипты загружаются в порядке, встречающемся на странице. Не имеет значения, является ли это внешним скриптом или встроенным скриптом - они выполняются в том порядке, в котором они встречаются на странице. Встроенные сценарии, которые приходят после внешних сценариев, сохраняются до тех пор, пока все внешние сценарии, предшествующие им, не загрузились и не запустились.

Асинхронные сценарии (независимо от того, как они указаны как асинхронные) загружаются и запускаются в непредсказуемом порядке. Браузер загружает их параллельно, и он может запускать их в любом порядке.

Там нет предсказуемого порядка среди нескольких асинхронных вещей. Если нужен предсказуемый порядок, его необходимо кодировать, регистрируя уведомления о загрузке из асинхронных сценариев и вручную упорядочивая вызовы javascript при загрузке соответствующих объектов.

Когда тег сценария вставляется динамически, поведение порядка выполнения будет зависеть от браузера. Вы можете увидеть, как Firefox ведет себя в этой справочной статье. Короче говоря, более новые версии Firefox по умолчанию динамически добавляют тег сценария для асинхронизации, если тег сценария не был установлен иначе.

Тег скрипта с async может быть запущен сразу после загрузки. Фактически, браузер может приостановить синтаксический анализатор от всего, что он делал, и запустить этот скрипт. Таким образом, он действительно может работать практически в любое время. Если сценарий был кэширован, он может запуститься практически сразу. Если для загрузки скрипта требуется некоторое время, он может запуститься после того, как парсер завершит работу. С TG43 следует помнить одну вещь: он может работать в любое время, и это время не предсказуемо.

Тег сценария с defer ждет, пока не будет выполнен весь синтаксический анализатор, а затем запускает все сценарии, отмеченные defer, в том порядке, в котором они были обнаружены. Это позволяет пометить несколько сценариев, которые зависят друг от друга, как defer. Все они будут отложены до тех пор, пока анализатор документов не будет завершен, но они будут выполняться в том порядке, в котором они были найдены, сохраняя свои зависимости. Я думаю о defer как о том, что скрипты помещены в очередь, которая будет обработана после того, как парсер будет сделан. Технически браузер может загружать сценарии в фоновом режиме в любое время, но он не будет выполнять или блокировать синтаксический анализатор до тех пор, пока синтаксический анализатор не завершит синтаксический анализ страницы и синтаксический анализ и запуск любых встроенных сценариев, которые не отмечены defer или async.

Вот цитата из этой статьи:

сценарии с вставленными сценариями выполняются асинхронно в IE и WebKit, но синхронно в Opera и Firefox до 4.0.

Соответствующая часть спецификации HTML5 (для новых совместимых браузеров) здесь. Там много написано об асинхронном поведении. Очевидно, что эта спецификация не применяется к более старым браузерам (или браузерам с плохим подтверждением), поведение которых вам, вероятно, придется проверить, чтобы определить.

Цитата из спецификации HTML5:

Затем первый из следующих вариантов, который описывает ситуацию необходимо соблюдать:

Если элемент имеет атрибут src, а элемент имеет задержку атрибут, и элемент был помечен как "вставленный парсер", и элемент не имеет асинхронного атрибута Элемент должен быть добавлен до конца списка скриптов, которые будут выполняться, когда документ завершил анализ, связанный с документом синтаксического анализатора, который создал элемент.

Задача, которую источник сетевой задачи помещает в очередь задач один раз алгоритм извлечения завершен должен установить элемент "готов к быть "выполненным парсером". Парсер будет обрабатывать выполнение скрипта.

Если элемент имеет атрибут src и элемент помечен как "вставленный парсером", и элемент не имеет асинхронного атрибута Элементом является ожидающий сценарий синтаксического анализа документа синтаксический анализатор, который создал элемент. (Может быть только один такой сценарий для каждого документа за раз.)

Задача, которую источник сетевой задачи помещает в очередь задач один раз алгоритм извлечения завершен должен установить элемент "готов к быть "выполненным парсером". Парсер будет обрабатывать выполнение скрипта.

Если элемент не имеет атрибута src, и элемент был помечается как "вставленный парсер", а документ HTML-парсера или Синтаксический анализатор XML, создавший элемент script, имеет таблицу стилей, которая блокирующие сценарии Элемент является ожидающим сценарием блокировки-разбора Документ синтаксического анализатора, который создал элемент. (Там может только быть одним из таких сценариев на каждый документ.)

Установите элемент "готов к выполнению парсера". Парсер будет обрабатывать выполнение сценария.

Если элемент имеет атрибут src, не имеет атрибута async, и не имеет установленного флага "force-async" Элемент должен быть добавлен до конца списка скриптов, которые будут выполняться по порядку, как только насколько это возможно, связанные с документом элемента скрипта на время запуска алгоритма подготовки сценария.

Задача, которую источник сетевой задачи помещает в очередь задач один раз алгоритм извлечения завершен должен выполнить следующие шаги:

Если элемент не является первым элементом в списке скриптов который будет выполнен по порядку как можно скорее, к которому он был добавлен выше, пометьте элемент как готовый, но прервите эти шаги без выполнение сценария еще.

Выполнение: Выполнить блок сценария, соответствующий первому сценарию. элемент в этом списке скриптов, которые будут выполняться по порядку, как только возможно.

Удалите первый элемент из этого списка скриптов, которые будут выполняться чтобы как можно скорее.

Если это список скриптов, которые будут выполняться по порядку в кратчайшие сроки еще не пуста, и первая запись уже помечена как готово, затем вернитесь к шагу с меткой выполнения.

Если элемент имеет атрибут src Элемент должен быть добавлен в набор сценариев, которые будут выполняться в кратчайшие сроки документа элемента сценария в то время, когда алгоритм подготовки сценария начал.

Задача, которую источник сетевой задачи помещает в очередь задач один раз алгоритм извлечения завершен должен выполнить блок сценария и затем удалите элемент из набора сценариев, которые будут выполняться как как можно скорее.

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


А как насчет скриптов модуля Javascript, type="module"?

Javascript теперь поддерживает загрузку модулей с помощью синтаксиса, подобного следующему:

<script type="module">
  import {addTextToBody} from './utils.mjs';

  addTextToBody('Modules are pretty cool.');
</script>

Или с атрибутом src:

<script type="module" src="http://somedomain.com/somescript.mjs">
</script>

Все, сценарии с type="module" автоматически получают атрибут defer. Это загружает их параллельно (если не встроено) с другой загрузкой страницы, а затем запускает их по порядку, но после того, как анализатор завершен.

Сценариям модуля также может быть присвоен атрибут async, который будет запускать встроенные сценарии модуля как можно скорее, не дожидаясь окончания синтаксического анализа и не ожидая запуска сценария async в каком-либо определенном порядке относительно других сценариев.

Есть довольно полезная временная диаграмма, которая показывает выборку и выполнение различных комбинаций сценариев, включая сценарии модуля, здесь, в этой статье: Загрузка модуля Javascript.

Ответ 2

Браузер выполнит скрипты в том порядке, в котором он их найдет. Если вы вызываете внешний script, он блокирует страницу до тех пор, пока не будет загружен и не запущен script.

Чтобы проверить этот факт:

// file: test.php
sleep(10);
die("alert('Done!');");

// HTML file:
<script type="text/javascript" src="test.php"></script>

Динамически добавленные скрипты выполняются, как только они добавляются в документ.

Чтобы проверить этот факт:

<!DOCTYPE HTML>
<html>
<head>
    <title>Test</title>
</head>
<body>
    <script type="text/javascript">
        var s = document.createElement('script');
        s.type = "text/javascript";
        s.src = "link.js"; // file contains alert("hello!");
        document.body.appendChild(s);
        alert("appended");
    </script>
    <script type="text/javascript">
        alert("final");
    </script>
</body>
</html>

Порядок оповещений "добавлен" → "привет!". → "final"

Если в script вы пытаетесь получить доступ к элементу, который еще не был достигнут (например: <script>do something with #blah</script><div id="blah"></div>), вы получите сообщение об ошибке.

В общем, да, вы можете включить внешние скрипты, а затем получить доступ к своим функциям и переменным, но только если вы выйдете из текущего тега <script> и запустите новый.

Ответ 4

После тестирования многих опций я обнаружил, что следующее простое решение загружает динамически загружаемые скрипты в порядке их добавления во все современные браузеры.

loadScripts(sources) {
    sources.forEach(src => {
        var script = document.createElement('script');
        script.src = src;
        script.async = false; //<-- the important part
        document.body.appendChild( script ); //<-- make sure to append to body instead of head 
    });
}

loadScripts(['/scr/script1.js','src/script2.js'])

Ответ 5

var scriptMap =["scriptUrl1","scriptUrl2", "scriptUrl3"];

var order = 0;

function loadScriptInOrder(){

    if(order == scriptMap.length) {

        return;

    }

    var JSLink = scriptMap[order];
    var JSElement = document.createElement('script');
    JSElement.src = JSLink;
    JSElement.onload = callback;
    document.getElementsByTagName('body')[0].appendChild(JSElement);

    function callback(){
        order++;
        loadScriptInOrder();
    }


};

loadScriptInOrder();

Ответ 6

есть идея, почему следующий код будет выполняться в разных браузерах в разном порядке? (код находится во внешнем файле и вставлен в виде обычного HTML-кода между <body></body> с использованием <script src="js/filename.js"></script>)

document.write("This is the first document.write()");

document.write("<br />followed by a second doc.write with br-element before");

document.write("<br /> and a third one doc.write with br before");

alert("now comes an alert() as fourth (will be expected)");

document.write("<br /> then there will be a fifth doc.write after the first alert");

document.write("<br />and a one more (sixth) doc.write after which an alert will be expected");

alert("the second alert at seventh position");

document.write("<br />and now the eight doc.write after the seventh alert");

В Chrome сначала выполняются оповещения, за которыми следуют docWrites, в Edge - первые 3 docWrite, затем оба оповещения, а затем - другой docWrite (также неправильный порядок). Только Firefox выполняет этот код в правильном порядке.

Есть идеи?