Делаем все скрипты сайта асинхронными без дополнительных фреймворков

У средних людей (таких, как я, например), синхронность ассоциируется с синхронным плаванием: когда ножки девушек над поверхностью воды одновременно показывают одинаковые фигуры. Т.е. слово «синхронно» становится синонимом слову «выполняется одновременно», потому что очень завораживают эти ножки...

Но для программиста синхронность — это последовательная цепь событий, когда за 1-м событием следует 2-е, а за 2-м — 3-е, но никак вдруг 3-е событие не выполнится раньше 2-го, даже если устанет ждать его окончания. Соответственно, асинхронность для программиста означает, что события выполняются параллельно, и возможно, что 3-е событие выполнится раньше 2-го и даже раньше 1-го. Посмотрим теперь, что синхронность и асинхронность значат для наших сайтов.

Если на вашем сайте используется конструкция вида:

<script src="..."></script>

А она наверняка используется раз 5-10. То браузер задерживает отрисовку страницы на этом месте и ждёт загрузки скрипта по ссылке. Иногда это может быть полезно. Например, у вас на сайте установлен JS-счётчик просмотров статей:

<script src="..."></script>
<script>document.write( view_count )</script>

Ваш скрипт при вызове устанавливает значение переменной view_count, которую вы далее выводите. Но, если вы будете использовать асинхронный код, то получите не количество просмотров, а ошибку. Потому что на загрузку вашего скрипта, указанного по ссылке src="...", понадобится время, так что document.write будет выполнен ещё до того, как будет задана переменная view_count.

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

Макет сайта

Допустим, у вас такой макет сайта, как показано на картинке. Где-то в тексте статьи или даже в её начале вы установили рекламу Google Adsense, потому что в этом месте она имеет максимальный CTR. Конечно, реклама при каждом показе страницы запрашивается с адресов Google. Происходит это обычно быстро, потому что большинство людей уже давно не пользуются модемным интернетом. Но у тех посетителей сайта, которые имеют нестабильное соединение, рекламный код загружается иногда и по несколько минут. А если на странице несколько рекламных блоков, а в конце ещё и форма комментариев ВКонтакте? Хотелось бы, чтобы посетитель не имел проблем с просмотром сайта, а определённый виджет отображался бы, как только будет загружен его код.

Идёт загрузка

Но происходит иначе. Посетитель видит такой вот «сломанный» сайт до тех пор, пока не будет получен код рекламного блока. Обычно это лишь несколько секунд (даже этого хотелось бы избежать), а бывает, что и несколько минут. Конечно, такое обстоятельство не способствует уменьшению показателя отказов. Не считаете ли вы такую цену слишком высокой за сомнительную радость посетителя во что бы то ни стало посмотреть рекламное объявление?

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

Организация асинхронности

В спецификации элемента script сказано, что достаточно указать атрибут async, чтобы скрипт загрузился асинхронно и выполнился сразу же после загрузки. Есть ещё атрибут defer — с ним скрипт тоже загружается асинхронно, но выполняется после загрузки страницы. Ура?! Не совсем. Объясню на примере виджета группы ВКонтакте. Вот его код, в котором я к вызову скрипта добавил атрибут async:

<script type="text/javascript" src="http://vk.com/js/api/openapi.js" async></script>
<script type="text/javascript">
VK.Widgets.Group( "vk_groups_...", { mode: 0, width: "165", height: "360" }, 485... );
</script>

Конечно, пример не рабочий (и не только из-за многоточий). Просто, к моменту обработки браузером строки «VK.Widgets.Group ( ... )» не будет ещё создан объект «VK» — он будет создан только после загрузки и выполнения «openapi.js», но тогда прорисовка страницы уже уйдёт вперёд. Если только мы не вызовем функцию VK.Widgets.Group( ... ) после того, как будет загружен скрипт «openapi.js». Но как определить этот момент? Даже если мы поместим вызов функции в самый конец страницы, не факт, что браузер успеет загрузить скрипт до того, как дорисует страницу до конца. Скажу более: эта статья как раз создана для того, чтобы браузер отрисовывал вашу страницу за доли секунды!

Асинхронный код виджета группы ВКонтакте

Победить созданную задачу поможет вот этот код:

<div id="vk_groups"></div>
<script type="text/javascript" src="http://vk.com/js/api/openapi.js" async></script>
<script type="text/javascript">
( function start() {
	if ( window.VK ) {
		VK.Widgets.Group( "vk_groups", { ваши_параметры }, ваш_id );
	} else setTimeout( start, 500 );
} )();
</script>

Принцип прост, как первый советский трактор. Мы выполняем проверку существования объекта «VK» в нашем окне, и если его ещё нет (значит, скрипт ВК ещё не загрузился), то назначаем повторную проверку через полсекунды. А когда при очередной проверке мы обнаружим объект «VK», вызовем функцию отображения группы ВКонтакте. Если вы правильно указали «ваши_параметры» и «ваш_id» (их скопируйте из оригинального кода, полученного на сайте vk.com), то код виджета группы ВКонтакте будет вызван асинхронно, и прорисовка страницы не приостановится на моменте вызова скрипта ни на секунду.

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

<script type="text/javascript">
google_ad_client = "ca-pub-...";
google_ad_slot = "...";
google_ad_width = x;
google_ad_height = y;
</script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js">

Асинхронный код рекламного блока Google Adsense

Здесь я ничего придумывать не буду, а приведу пример кода из справочного раздела официального сайта Google:

<ins class="adsbygoogle"
style="display:block; width:728px; height:90px"
data-ad-client="ca-pub-..."
data-ad-slot="..."></ins>
<script>
( adsbygoogle = window.adsbygoogle || [] ).push( {} );
</script>

И ещё обязательно надо произвести асинхронную загрузку скрипта Google Adsense. На всякий случай, лучше в конце страницы (т.е. после самого последнего рекламного блока в коде страницы):

<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" async></script>

Если мне не верите или сомневаетесь (всё же, дело касается ваших денег), вот официальный источник — посмотрите там, заодно прочитайте ответы на часто задаваемые вопросы.

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

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

Для разнообразия приведу пример вызова блока комментариев ВКонтакте с асинхронной загрузкой при помощи jQuery.

Асинхронный код комментариев ВКонтакте

комментарии ВКонтакте

Если вы ещё не используете на сайте комментарии ВКонтакте, вам сначала надо зарегистрировать сайт. Для регистрации сайта идём на эту страницу (требуется авторизация). И указываем необходимые параметры.

При этом вы получите синхронный код, который вам надо внимательно посмотреть, чтобы решить, какие настройки блока комментариев ВКонтакте будут для вас индивидуальны — вам надо будет их вписать в мой асинхронный код:

<div id="vk_comments"></div>
<script type="text/javascript">
( function( $j ) { $j( function() {
	$j.ajax( {
		url: 'vk.com/js/api/openapi.js',
		cache: true,
		dataType: 'script',
		success: function() {
			VK.init( { apiId: ваш_id, onlyWidgets: true } );
			VK.Widgets.Comments( "vk_comments", { limit: 4, width: "640", attach: "*" } );
		}
	} );
} ) } )( jQuery );
</script>

Как вы догадались, вместо ваш_id используется ваш id, а вместо limit: 4, width: "640", attach: "*" — ваши настройки. И ещё, если вы на одной странице используете и комментарии ВКонтакте, и виджет, то в последнем примере рядом с вызовом функции VK.Widgets.Comments добавьте и вызов VK.Widgets.Group.

Кстати, в jQuery есть функция getScript(), которую можно использовать вместо ajax()... Но не нужно. :) Несмотря на то, что функция getScript() предназначена специально для загрузки скриптов с их последующим выполнением (частный случай функции ajax()), она не кэширует загружаемые скрипты. Т.е. скрипт будет загружаться при каждой загрузке страницы. В моём же примере скрипт будет загружен только при первом запросе (возможно, этот скрипт уже загружен при посещении другого сайта с виджетом ВКонтакте) — на что потратятся секунды, а в дальшейшем будет браться из кэша браузера, для чего достаточно долей секунды.

На этом «продвинутые» веб-мастеры отправляются ковырять код своих сайтов, а не столь продвинутые с благоговением думают, сколько же в мире всего интересного и непостижимого! Но и тех, и других я жду в комментариях. ;) Напишите, вызов каких скриптов у вас осуществляется синхронно, а вы хотели бы сделать (или сделали) асинхронным.

Кстати, я тут как сапожник без запог, потому что на этом сайте у меня скрипты загружаются не асинхронно. :) Ведь в первую очередь на доработку отправляются приносящие доход сайты, а этот сайт у меня только время отнимает. Вот пример сайта, на котором все скрипты загружаются асинхронно, а именно: jquery, yandex metrika, yandex context, adsbygoogle, vk openapi, functions.js, share42 (социальные кнопки). Как это реализовано и как синхронизирована работа скриптов — без труда читается в исходном коде страницы и в файле functions.js.

Запись опубликована в рубрике Web-мастеринг с метками , . Короткая ссылка для добавления в закладки: Делаем все скрипты сайта асинхронными без дополнительных фреймворков.

31 Responses

  1. Константин говорит:

    по сути, и код виджета группы можно сделать через $.ajax, но как я понял это было в качестве примера одного из вариантов. В целом интересная статья получилась. Я обычно в конец страницы скрипт ставил и потом переносил результат в нужное место, что-то вроде такого:

    $(document).ready(function(){ $('#linkwall2').detach().appendTo('#linkwall'); });

    • Павлуха говорит:

      Константин, я так и делаю — через $.ajax. :) Потому как jQuery использую почти во всех создаваемых темах для более стратегических целей, которые чистым жаваскриптом решаются сложно (будь то слайдер, анимированная подсказка или «поплавок»). Но в статье я на этом варианте не настаивал, потому как только ради асинхронной загрузки не стоит нагружать сайт дополнительным скриптом, размер которого около 100 КБ. Есть и другие способы, которые я подглядел в кодах скриптов Яндекса и Гугла.

      А с переносом вариант тоже раньше использовал. Перенос, конечно, способствует тому, чтобы полезный контент загрузился до того, как начнут загружаться внешние коды — чтобы посетитель не скучал в ожидании их загрузки. Но есть подвох: синхронные скрипты, пусть они в самом конце страницы — будут загружаться последовательно. Сначала первый 3-10 секунд, потом втророй, третий... В итоге, событие $(document).ready() сработает только после загрузки всех этих скриптов. К тому времени может пройти полминуты или минута. В таком случае трудно доверить какие-то важные махинации с деревом html-элементов скриптам, которые срабатывают после запуска $(document).ready() — процентов 20 посетителей даже не дождутся самых крутых «спецэффектов».

      Фишка перехода на асинхрон как раз в том, что $(document).ready() срабатывает в считанные секунды (и даже доли секунды) после запроса страницы. В этом вся круть! — Максимально сократить время ожидания события «ready» на документе ;)

  2. Хлебослав говорит:

    А как понять что код стал асинхронным? ) Сделал, работает контактогруппа с вашими дополнениями в коде, но не пойму, изменилось ли что)

  3. Хлебослав говорит:

    Код как раз над контентом, поэтому в некоторые моменты выходит, что контент не грузится, ждёт группу. Ужас... Переместить — не вариант. Удалить — вариант, но жалко. Вот и решил найти выход... И не знаю, нашёл ли )

  4. Хлебослав говорит:

    Перегружаю страницу, пытаюсь подсмотреть, но интернет, сука, слишком быстрый ща ))

    (Сорри. Это последний коммент — знаю, что не чат :-0)

  5. Алесандр говорит:

    Привет, как сделать асинхронным код типа: ....... . Это постраничная навигация, вставляю async и она исчезает.

  6. Алесандр говорит:

    Вместо кода отображаются звёздочки. Я заполнил поле сайт, в исходном коде поищи jquery/1.9.1/jquery.min.js, только это уже не постраничная навигация, а ккая-то другая фигня.

    • Павлуха говорит:

      Если после редакции файлов появляются звёздочки или ромбики с вопросиками внутри — значит файл редактировался в программе, не поддерживающей кодировку UTF-8 без BOM. Либо сохранён файл неправильно. Надо юзать notepad++ (именно с плюсами) и иже с ним.

  7. DNX говорит:

    Привет! Это Lisena.

    Слушай, а ты, не безвозмездно, само собой, можешь с асинхронным кодом помочь?

    А то у меня тут вот: developers.google.com/spe...&tab=desktop

    В общем, допилить надо, а у меня мозгов не хватает

  8. Вадим говорит:

    Асинхронная загрузка скриптов Джумла 2,5 вирталмарт 2.0.6

    иду сюда libraries\joomla\document\document.php

    на строке 461

    код

    public function addScript($url, $type = "text/javascript", $defer = false, $async = false) { $this->_scripts[$url]['mime'] = $type; $this->_scripts[$url]['defer'] = $defer; $this->_scripts[$url]['async'] = $async; return $this; }

    меняю на

    public function addScript($url, $type = "text/javascript", $defer = true, $async = false) { $this->_scripts[$url]['mime'] = $type; $this->_scripts[$url]['defer'] = $defer; $this->_scripts[$url]['async'] = $async; return $this;

    Скрипты грузятся асинхронно но вместе с этим приходят проблемы. Не добавляется товар в корзину в internet explorer и мозиле. В опере всё работает нормально.

    Помогите разобраться.

    • Павлуха говорит:

      Лично для меня в Жумле всё очень сложно. Потому я вам не помогу.

      Но в общем случае проблемы перехода на асинхрон возникают из-за того, что скрипты выполняются не вовремя. Например, вызвана js-функция работы с корзиной, а js-файл, где эта функция описана, ещё не загружен. Это вызывает фатальную ошибку парсинга js, и выполнение js-кода на этом останавливается.

      Решение — вызывать функции только тогда, когда загружен js-файл с их описанием. Например, используя таймер, проверять каждую секунду, не загрузился ли файл, т.е. существует ли функция: if ( function != typeof my_function ) setTimeout( ... )

  9. Марат говорит:

    Здравствуйте!

    Подключил форму комментирования Disqus на своем блоге (free-pc.ru). Чуть выше блока Disqus у меня стоят комментарии Вконтакте. Так вот после установки Disqus, комментарии в контакте то грузятся, то не грузятся... (я заметил, что при первой загрузке страницы грузятся, после повторной загрузки — не грузятся) Что ему не нравится?. Поможет ли мне Ваш асинхронный код? Блог: free-pc.ru

    • Павлуха говорит:

      Марат, могу предположить, в чём загвоздка. У вас есть плагин «Social Locker», который активирует приложение комментариев ВКонтакте. Указывает apiId: 4777887 (и, кстати, подгружает асинхронно). Потом дальше в коде комментарии ВКонтакте инициируются ещё раз после комментария Скрипт от VK.com (Комментарии) — возможно, это ручная вставка. Здесь уже скрипт работает не асинхронно и указывает apiId: 4367436. Один из указанных apiId неверный. Вероятно, загрузятся комментарии или нет, зависит от того, какая из инициаций произойдёт быстрее. И если под «не грузятся» вы понимаете вывод сообщения «invalid domain» в месте вывода комментариев, это только подкрепляет мою версию

  10. Tatiana говорит:

    Здравствуйте!

    Надеюсь, что Вы мне поможете, наконец, понять почему у меня постоянно возникает ошибка 404 (вижу ее в логах), которая связана с асинхронными кодами: не найден ​/pagead2.googlesyndication.com​/pagead​/js​/adsbygoogle.js. У меня на сайте стоят 3 кода от адсенс (один из них в сайдбаре) и все они ассинхронные. В чем тут может быть дело? Сайт работает на WordPress. Заранее спасибо большое!

    • Павлуха говорит:

      Мало информации. Нужно пример строки лога. Нужно понаблюдать, при запросе какой страницы генерируются ошибки

  11. Дмитрий говорит:

    У вас код на странице нельзя скопировать. Неудобно. Если кто то захочет стырить ваши тексты такой подход его точно не остановит.

    • Павлуха говорит:

      Дмитрий, а выделять левой кнопкой мыши, кликать по выделению правой кнопкой мыши и жать «копировать» пробовали?

  12. Евгений говорит:

    Спасибо! В очередной раз отвалился Вконтакт, и дальше не шла загрузка, воспользовался вашим советом.

  13. Avto говорит:

    Блин очень круто спасибо! А выложите пожалуйста такой же асинхронник для FACEBOOK?

  14. Евгений говорит:

    К сожалению Ваш код не заработал. А стандартный нормально. Может за время прошедшее с публикации статьи что-то поменялось с кодами вызова комментариев Вконтакте?

    • Павлуха говорит:

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

      А насчёт вопроса — даже если что-то поменялось, этот код до сих пор успешно работает на нескольких сайтах, куда я его разместил.

  15. Дмитрий говорит:

    Доброго дня!

    Прочитав статью, заменил свой код:

    VK.init({apiId: 5204731, onlyWidgets: true});

    на

    ( function( $j ) { $j( function() { $j.ajax( { url: 'vk.com/js/api/openapi.js?121', cache: true, dataType: 'script', success: function() { VK.init( { apiId: 5204731, onlyWidgets: true } ); } } ); } ) } )( jQuery );

    Результат — перестал подгружаться контактовский блок «нравится». В чем ошибка? (cms wordpress)

  16. Дмитрий говорит:

    нашел решение здесь: habrahabr.ru/post/166831/

    • Иван говорит:

      В чем решение? Вы просто использовали коды с хабра? Или дополнили местный? Подскажите, пожалуйста.

      • Павлуха говорит:

        Проблема в том, что синхронные скрипты задерживают отрисовку страницы, иногда визуально ломая вёрстку. Решение — сделать все скрипты сайта асинхронными. Я показал на двух примерах популярных скриптов, как это сделать, а также дал в конце ссылку на свой сайт, где десяток разных скриптов настроены на асинхронную загрузку. Кому необходимо, тот разберётся или задаст конкретный вопрос по конкретному скрипту

  17. Иван говорит:

    Павлуха, отличный пост с рекомендациями — спасибо!

    Но как быть с ЛАЙКами от ВК, куда их впихнуть?

    Подскажи, пожалуйста.

    • Павлуха говорит:

      По аналогии с комментариями VK — этот пример рассмотрен в статье

  18. Евгений говорит:

    А с чего Вы взяли, что Ваш асинхронный код работает? Во-первых сайт про кулинарию не так уж быстро грузится. Во-вторых если проверить страничку с рецептом на webpagetest.org, то «Document Complete» наступает после того, как загрузятся скрипты и картинки из ВК. Зато googleads.g.doubleclick.net — это без вопросов, грузится после того как страничка отрисована.

  19. Поцык говорит:

    Спасибо, советский трактор с асинхронной загрузкой ВК сработал!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Иногда ваш комментарий может не отобразиться сразу после публикации - будто пропал. Не волнуйтесь, он не пропадёт и появится потом, после моего одобрения.