Динамическое дополнение сценария должно быть заказано?
Я добавляю некоторые <script>
динамически привязывает элемент head после загрузки страницы. Я понимаю, что скрипты загружаются асинхронно, но могу ли я ожидать, что они будут проанализированы в порядке их добавления?
Я вижу ожидаемое поведение в Firefox, но не в Safari или Chrome. Глядя на документ в инструментах разработчика Chrome и Firebug, оба показывают следующее:
<html>
<head>
...
<script type="text/javascript" src="A.js"></script>
<script type="text/javascript" src="B.js"></script>
</head>
...
</html>
Однако, глядя на представление загрузки ресурсов, кажется, что chrome выполняет синтаксический анализ в зависимости от того, что возвращается первым с сервера, а firebug всегда загружает их в том порядке, в котором были добавлены теги сценариев, даже когда B возвращается первым с сервера.
Стоит ли ожидать, что Chrome/Safari проанализирует файлы в указанном порядке? Использование бета-версии Chrome 5.0.375.29 на OS X 10.6.3
РЕДАКТИРОВАТЬ (10/10/10): Когда я говорю синтаксический анализ, я имею в виду выполнить - можно увидеть много преимуществ агрессивного анализа - thx rikh
РЕДАКТИРОВАТЬ (11/5/10): Хорошо, так что я собрал тест по тому же принципу, что и Juandopazo ниже. Однако я добавил комбинацию вещей, в том числе
- Добавление элемента script к голове напрямую с помощью javascript. (Тесты A -> D)
- Добавление элемента script в заголовок с помощью метода append() jquery. (Тесты E -> H)
- "Загрузка" скрипта с помощью метода getScript() jquery. (Тесты I -> L)
Я также попробовал все комбинации атрибутов "async" и "defer" в тегах сценария.
Вы можете получить доступ к тесту здесь - http://dyn-script-load.appspot.com/ и просмотреть исходный код, чтобы увидеть, как он работает. Загруженные скрипты просто вызывают функцию update().
Первое, на что следует обратить внимание, это то, что только 1 -й и 3 -й методы, описанные выше, работают параллельно - 2 -й выполняет запросы последовательно. Вы можете увидеть график этого здесь -
Рисунок 1 - График жизненного цикла запроса
http://dyn-script-load.appspot.com/images/dynScriptGraph.png
Также интересно, что подход jquery append() также блокирует вызовы getScript() - вы можете видеть, что ни один из них не выполняется до тех пор, пока все вызовы append() не будут завершены, а затем все они будут выполняться параллельно. В заключение отметим, что метод jQuery append(), по-видимому, удаляет теги сценария из заголовка документа после их выполнения. Только первый метод оставляет теги script в документе.
Результаты Chrome
В результате Chrome всегда выполняет первый возвращаемый скрипт, независимо от теста. Это означает, что все тесты "провалились", кроме метода jQuery append().
Изображение 2 - Результаты Chrome 5.0.375.29 beta
http://dyn-script-load.appspot.com/images/chromeDynScript.png
Результаты Firefox
Однако в Firefox кажется, что если используется первый метод, а async имеет значение false (т.е. не установлено), то сценарии будут надежно выполняться по порядку.
Изображение 3 - Результаты FF 3.6.3
http://dyn-script-load.appspot.com/images/ffDynScript.png
Обратите внимание, что Safari, похоже, дает разные результаты так же, как Chrome, что имеет смысл.
Кроме того, у меня есть только задержка 500 мс на медленном скрипте, просто чтобы уменьшить время начала -> окончания. Возможно, вам придется обновить пару раз, чтобы Chrome и Safari перестали работать во всем.
Мне кажется, что без метода для этого мы не используем возможность параллельного извлечения данных, и нет никаких причин, по которым мы не должны этого делать (как показывает Firefox).
6 ответов
Извините за ответ на мой собственный вопрос, но это было какое-то время, и мы нашли решение. Мы пришли к тому, чтобы одновременно загружать javascript как текст, содержащийся в объекте json, а затем использовать eval(), как только они были загружены, чтобы выполнить их в правильном порядке. Параллельная загрузка плюс заказное исполнение. В зависимости от вашего варианта использования вам может не понадобиться JSON. Грубо, вот код, который показывает, что мы сделали -
// 'requests' is an array of url's to javascript resources
var loadCounter = requests.length;
var results = {};
for(var i = 0; i < requests.length; i++) {
$.getJSON(requests[i], function(result) {
results[result.id] = result;
...
if(--loadCounter == 0) finish();
});
}
function finish() {
// This is not ordered - modify the algorithm to reflect the order you want
for(var resultId in results) eval(results[resultId].jsString);
}
Вы можете загрузить b.js из a.js, чтобы быть на 100% уверенным... хотя я бы сам хотел получить окончательный ответ на этот вопрос, особенно с синхронизацией загрузки скриптов ajax.
Порядок загрузки и порядок выполнения - это не одно и то же. На вашей странице, даже если B.js загружается первым, движок браузера будет ждать, пока A.js продолжит обработку страницы.
Сценарии определенно обрабатываются не только в том порядке, в котором они появились в документе, но и в том месте, где они появились.
Представьте, что если бы это было не так, было бы много ошибок, если бы ваш маленький скрипт, использующий jQuery, был загружен и обработан до библиотеки jQuery.
Кроме того, когда вы делаете "document.write" в js-файле, он появляется там, где был объявлен скрипт. Вы также не можете получить доступ к объектам DOM, которые появляются после объявления скрипта.
Вот почему есть рекомендации размещать скрипты в самом низу страницы, чтобы предотвратить их слишком быстрое выполнение и уменьшить "воспринимаемое время загрузки" страницы, потому что движок рендеринга браузера останавливается, как только скрипт обрабатывается.
Майк
РЕДАКТИРОВАТЬ: если они добавляются динамически с Javascript, я думаю, что они обрабатываются в порядке, они были добавлены во времени.
Нет, вы не можете ожидать, что все браузеры будут откладывать выполнение обоих скриптов до тех пор, пока оба не будут загружены (** особенно, когда вы добавляете их динамически).
Если вы хотите выполнить код в B.js
только после A.js
загружается, то лучше всего добавить onload
обратный звонок в A.js
который устанавливает переменную, а другой B.js
которая проверяет, установлена ли эта переменная, затем выполняет необходимую функцию в B.js
если есть (и если A.js
не загружен, запускается таймер, который периодически проверяет, пока не загрузится).
Насколько я понимаю, они должны выполняться в том порядке, в котором они указаны в документе. Некоторые браузеры могут выполнять разбор не по порядку, но их все равно придется выполнять в правильном порядке.
Я исследовал это, работая над небольшой библиотекой, которая динамически загружает модули, такие как YUI 3. Я создал небольшой тест, который загружает два сценария, которые просто вставляют содержимое в div. Один из них - это обычный файл JS, а другой - файл PHP, ожидающий выполнения в течение 3 секунд.
http://www.juandopazo.com.ar/tests/asyn-script-test.html
Как видите, сценарии выполняются по окончании загрузки, а не в том порядке, в котором вы добавляете их в DOM в каждом браузере.