Можно ли доверять script.readyState для определения окончания динамической загрузки скрипта?

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

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

LABjs (и многие другие загрузчики) устанавливают "onload" и "onreadystatechange" для всех элементов скрипта, зная, что некоторые браузеры будут запускать один, а другие - другой...

Вы можете найти пример этого в текущей версии jQuery на момент написания, v1.3.2:

// Attach handlers for all browsers
script.onload = script.onreadystatechange = function(){
    if ( !done && (!this.readyState ||
    this.readyState == "loaded" || this.readyState == "complete") ) {
        done = true;
        success();
        complete();

        // Handle memory leak in IE
        script.onload = script.onreadystatechange = null;
        head.removeChild( script );
    }
};

Это состояние дел, но во время анализа странного поведения в Opera 9.64 я пришел к выводу, что при использовании этого метода обратный вызов onload срабатывает слишком рано.

Я опубликую свои собственные выводы в ответ на этот вопрос и хотел бы собрать дополнительные доказательства и отзывы сообщества.

5 ответов

Решение

В Opera нельзя доверять свойству script.readyState. Например, readyState "загружен" может быть запущен до запуска скрипта в Opera 9.64.

Я выполнил один и тот же тест в Opera 9.64 и Opera 10 с разными результатами.

В Opera 9.64 обработчик onreadystatechange запускается дважды, один раз до и один раз после запуска скрипта. Свойство readyState "загружено" в обоих случаях, что означает, что этому значению нельзя доверять для определения конца загрузки скрипта:

# Fri Dec 18 2009 17:54:43 GMT+0100
# Opera/9.64 (Windows NT 5.1; U; en) Presto/2.1.1
Test for script.readyState behavior started
Added script with onreadystatechange handler
readystatechange: loaded
test1.js: Start
test1.js: Start of closure
test1.js: End of closure
readystatechange: loaded

В Opera 10 обработчик onreadystatechange по-прежнему запускается дважды со значением "загружен", но оба раза после запуска сценария:

# Fri Dec 18 2009 18:09:58 GMT+0100
# Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.10
Test for script.readyState behavior started
Added script with onreadystatechange handler
test1.js: Start
test1.js: Start of closure
test1.js: End of closure
readystatechange: loaded
readystatechange: loaded

Это различное поведение указывает на то, что onreadystatechange не является надежным способом определения окончания загрузки скрипта в Opera. Так как Opera также поддерживает прослушиватель onload, вместо этого следует использовать этот другой механизм.

На основании результатов этих тестов onreadystatechange следует использовать только для определения окончания загрузки скрипта в Internet Explorer, и его нельзя устанавливать в других браузерах.

В Firefox, Safari и Chrome вызывается never обработчика onreadystatechange.

Я создал короткий контрольный пример, создав динамический сценарий только с набором обработчиков onreadystatechange:

<script type="text/javascript" language="javascript">
bezen.log.info(new Date(),true);
bezen.log.info(navigator.userAgent,true);

// Activate logs
bezen.log.on();
bezen.log.info('Test for script.readyState behavior started');

var script = document.createElement('script');
script.src = 'test1.js';
script.onreadystatechange = function(){
  bezen.log.info('readystatechange: '+script.readyState);
};
document.body.appendChild(script);
bezen.log.info('Added script with onreadystatechange handler');
</script>

Я выполнил тест для локального файла в Firefox 2, Firefox 3, Firefox 3.5, Safari 3, Safari 4 и Chrome 3 и получил похожие результаты (здесь логи, записанные в FF 3.5):

Fri Dec 18 2009 17:53:58 GMT+0100
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6
Test for script.readyState behavior started
Added script with onreadystatechange handler
test1.js: Start
test1.js: Start of closure
test1.js: End of closure

Onreadystatechange никогда не вызывается. В этих браузерах только прослушиватель onload полезен для определения окончания загрузки скрипта, onreadystatechange не требуется.

Я обнаружил, что Internet Explorer (тестирование в 9) НЕ ВСЕГДА готовит ваш скрипт, когда readyState === 'загружен'. Я имел успех, используя этот обработчик событий (в 9, конечно) onactivate. Вытаскивал мои волосы раньше.

В Internet Explorer обработчик onreadystatechange срабатывает, как и ожидалось, после завершения сценария.

Я выполнил тот же тест в Internet Explorer 6, Internet Explorer 7 и Internet Explorer 8, с похожими результатами в этих трех браузерах (здесь журналы, записанные в Internet Explorer 6):

Fri Dec 18 18:14:51 UTC+0100 2009
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Test for script.readyState behavior started
Added script with onreadystatechange handler
test1.js: Start
test1.js: Start of closure
test1.js: End of closure
readystatechange: complete

Здесь, при тестировании с использованием локального файла, readyState всегда "завершен", и он был прежним после обновления нескольких страниц.

Однако, как отметил в этом посте Николас С. Закас, вы также можете наблюдать "загруженный" и "полный" или просто "загруженный" при других обстоятельствах.

Подобные результаты в Chrome.

Не берут уже...

Просто onload и onerror.

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