Динамическое дополнение сценария должно быть заказано?

Я добавляю некоторые <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 ниже. Однако я добавил комбинацию вещей, в том числе

  1. Добавление элемента script к голове напрямую с помощью javascript. (Тесты A -> D)
  2. Добавление элемента script в заголовок с помощью метода append() jquery. (Тесты E -> H)
  3. "Загрузка" скрипта с помощью метода 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 в каждом браузере.

Другие вопросы по тегам