Скрипты: async, defer
В современных сайтах скрипты обычно «тяжелее», чем HTML: они весят больше, дольше обрабатываются.
Когда браузер загружает HTML и доходит до тега <script>. </script> , он не может продолжать строить DOM. Он должен сначала выполнить скрипт. То же самое происходит и с внешними скриптами <script src=". "></script> : браузер должен подождать, пока загрузится скрипт, выполнить его, и только затем обработать остальную страницу.
Это ведёт к двум важным проблемам:
- Скрипты не видят DOM-элементы ниже себя, поэтому к ним нельзя добавить обработчики и т.д.
- Если вверху страницы объёмный скрипт, он «блокирует» страницу. Пользователи не видят содержимое страницы, пока он не загрузится и не запустится:
Конечно, есть пути, как это обойти. Например, мы можем поместить скрипт внизу страницы. Тогда он сможет видеть элементы над ним и не будет препятствовать отображению содержимого страницы:
Но это решение далеко от идеального. Например, браузер замечает скрипт (и может начать загружать его) только после того, как он полностью загрузил HTML-документ. В случае с длинными HTML-страницами это может создать заметную задержку.
Такие вещи незаметны людям, у кого очень быстрое соединение, но много кто в мире имеет медленное подключение к интернету или использует не такой хороший мобильный интернет.
К счастью, есть два атрибута тега <script> , которые решают нашу проблему: defer и async .
defer
Атрибут defer сообщает браузеру, что он должен продолжать обрабатывать страницу и загружать скрипт в фоновом режиме, а затем запустить этот скрипт, когда DOM дерево будет полностью построено.
Вот тот же пример, что и выше, но с defer :
- Скрипты с defer никогда не блокируют страницу.
- Скрипты с defer всегда выполняются, когда дерево DOM готово, но до события DOMContentLoaded .
Следующий пример это показывает:
- Содержимое страницы отобразится мгновенно.
- Событие DOMContentLoaded подождёт отложенный скрипт. Оно будет сгенерировано, только когда скрипт (2) будет загружен и выполнен.
Отложенные с помощью defer скрипты сохраняют порядок относительно друг друга, как и обычные скрипты.
Поэтому, если сначала загружается большой скрипт, а затем меньшего размера, то последний будет ждать.
Браузеры сканируют страницу на предмет скриптов и загружают их параллельно в целях увеличения производительности. Поэтому и в примере выше оба скрипта скачиваются параллельно. small.js скорее всего загрузится первым.
Но спецификация требует последовательного выполнения скриптов согласно порядку в документе, поэтому он подождёт выполнения long.js .
Атрибут defer будет проигнорирован, если в теге <script> нет src .
async
Атрибут async означает, что скрипт абсолютно независим:
- Страница не ждёт асинхронных скриптов, содержимое обрабатывается и отображается.
- Событие DOMContentLoaded и асинхронные скрипты не ждут друг друга:
- DOMContentLoaded может произойти как до асинхронного скрипта (если асинхронный скрипт завершит загрузку после того, как страница будет готова),
- …так и после асинхронного скрипта (если он короткий или уже содержится в HTTP-кеше)
Так что если у нас есть несколько скриптов с async , они могут выполняться в любом порядке. То, что первое загрузится – запустится в первую очередь:
- Содержимое страницы отображается сразу же : async его не блокирует.
- DOMContentLoaded может произойти как до, так и после async , никаких гарантий нет.
- Асинхронные скрипты не ждут друг друга. Меньший скрипт small.js идёт вторым, но скорее всего загрузится раньше long.js , поэтому и запустится первым. То есть, скрипты выполняются в порядке загрузки.
Асинхронные скрипты очень полезны для добавления на страницу сторонних скриптов: счётчиков, рекламы и т.д. Они не зависят от наших скриптов, и мы тоже не должны ждать их:
Динамически загружаемые скрипты
Мы можем также добавить скрипт и динамически, с помощью JavaScript:
Скрипт начнёт загружаться, как только он будет добавлен в документ (*) .
Динамически загружаемые скрипты по умолчанию ведут себя как «async».
- Они никого не ждут, и их никто не ждёт.
- Скрипт, который загружается первым – запускается первым (в порядке загрузки).
Мы можем изменить относительный порядок скриптов с «первый загрузился – первый выполнился» на порядок, в котором они идут в документе (как в обычных скриптах) с помощью явной установки свойства async в false :
Например, здесь мы добавляем два скрипта. Без script.async=false они запускались бы в порядке загрузки ( small.js скорее всего запустился бы раньше). Но с этим флагом порядок будет как в документе:
Итого
У async и defer есть кое-что общее: они не блокируют отрисовку страницы. Так что пользователь может просмотреть содержимое страницы и ознакомиться с ней сразу же.
Но есть и значимые различия:
Порядок DOMContentLoaded async Порядок загрузки (кто загрузится первым, тот и сработает). Не имеет значения. Может загрузиться и выполниться до того, как страница полностью загрузится. Такое случается, если скрипты маленькие или хранятся в кеше, а документ достаточно большой. defer Порядок документа (как расположены в документе). Выполняется после того, как документ загружен и обработан (ждёт), непосредственно перед DOMContentLoaded . Пожалуйста, помните, что когда вы используете defer , страница видна до того, как скрипт загрузится.
Пользователь может знакомиться с содержимым страницы, читать её, но графические компоненты пока отключены.
Поэтому обязательно должна быть индикация загрузки, нерабочие кнопки – отключены с помощью CSS или другим образом. Чтобы пользователь явно видел, что уже готово, а что пока нет.
На практике defer используется для скриптов, которым требуется доступ ко всему DOM и/или важен их относительный порядок выполнения.
А async хорош для независимых скриптов, например счётчиков и рекламы, относительный порядок выполнения которых не играет роли.
Scripts: async, defer
In modern websites, scripts are often “heavier” than HTML: their download size is larger, and processing time is also longer.
When the browser loads HTML and comes across a <script>. </script> tag, it can’t continue building the DOM. It must execute the script right now. The same happens for external scripts <script src=". "></script> : the browser must wait for the script to download, execute the downloaded script, and only then can it process the rest of the page.
That leads to two important issues:
- Scripts can’t see DOM elements below them, so they can’t add handlers etc.
- If there’s a bulky script at the top of the page, it “blocks the page”. Users can’t see the page content till it downloads and runs:
There are some workarounds to that. For instance, we can put a script at the bottom of the page. Then it can see elements above it, and it doesn’t block the page content from showing:
But this solution is far from perfect. For example, the browser notices the script (and can start downloading it) only after it downloaded the full HTML document. For long HTML documents, that may be a noticeable delay.
Such things are invisible for people using very fast connections, but many people in the world still have slow internet speeds and use a far-from-perfect mobile internet connection.
Luckily, there are two <script> attributes that solve the problem for us: defer and async .
defer
The defer attribute tells the browser not to wait for the script. Instead, the browser will continue to process the HTML, build DOM. The script loads “in the background”, and then runs when the DOM is fully built.
Here’s the same example as above, but with defer :
- Scripts with defer never block the page.
- Scripts with defer always execute when the DOM is ready (but before DOMContentLoaded event).
The following example demonstrates the second part:
- The page content shows up immediately.
- DOMContentLoaded event handler waits for the deferred script. It only triggers when the script is downloaded and executed.
Deferred scripts keep their relative order, just like regular scripts.
Let’s say, we have two deferred scripts: the long.js and then small.js :
Browsers scan the page for scripts and download them in parallel, to improve performance. So in the example above both scripts download in parallel. The small.js probably finishes first.
…But the defer attribute, besides telling the browser “not to block”, ensures that the relative order is kept. So even though small.js loads first, it still waits and runs after long.js executes.
That may be important for cases when we need to load a JavaScript library and then a script that depends on it.
The defer attribute is ignored if the <script> tag has no src .
async
The async attribute is somewhat like defer . It also makes the script non-blocking. But it has important differences in the behavior.
The async attribute means that a script is completely independent:
- The browser doesn’t block on async scripts (like defer ).
- Other scripts don’t wait for async scripts, and async scripts don’t wait for them.
- DOMContentLoaded and async scripts don’t wait for each other:
- DOMContentLoaded may happen both before an async script (if an async script finishes loading after the page is complete)
- …or after an async script (if an async script is short or was in HTTP-cache)
In other words, async scripts load in the background and run when ready. The DOM and other scripts don’t wait for them, and they don’t wait for anything. A fully independent script that runs when loaded. As simple, as it can get, right?
Here’s an example similar to what we’ve seen with defer : two scripts long.js and small.js , but now with async instead of defer .
They don’t wait for each other. Whatever loads first (probably small.js ) – runs first:
- The page content shows up immediately: async doesn’t block it.
- DOMContentLoaded may happen both before and after async , no guarantees here.
- A smaller script small.js goes second, but probably loads before long.js , so small.js runs first. Although, it might be that long.js loads first, if cached, then it runs first. In other words, async scripts run in the “load-first” order.
Async scripts are great when we integrate an independent third-party script into the page: counters, ads and so on, as they don’t depend on our scripts, and our scripts shouldn’t wait for them:
Just like defer , the async attribute is ignored if the <script> tag has no src .
Dynamic scripts
There’s one more important way of adding a script to the page.
We can create a script and append it to the document dynamically using JavaScript:
The script starts loading as soon as it’s appended to the document (*) .
Dynamic scripts behave as “async” by default.
- They don’t wait for anything, nothing waits for them.
- The script that loads first – runs first (“load-first” order).
This can be changed if we explicitly set script.async=false . Then scripts will be executed in the document order, just like defer .
In this example, loadScript(src) function adds a script and also sets async to false .
So long.js always runs first (as it’s added first):
Without script.async=false , scripts would execute in default, load-first order (the small.js probably first).
Again, as with the defer , the order matters if we’d like to load a library and then another script that depends on it.
Summary
Both async and defer have one common thing: downloading of such scripts doesn’t block page rendering. So the user can read page content and get acquainted with the page immediately.
But there are also essential differences between them:
Order DOMContentLoaded async Load-first order. Their document order doesn’t matter – which loads first runs first Irrelevant. May load and execute while the document has not yet been fully downloaded. That happens if scripts are small or cached, and the document is long enough. defer Document order (as they go in the document). Execute after the document is loaded and parsed (they wait if needed), right before DOMContentLoaded . In practice, defer is used for scripts that need the whole DOM and/or their relative execution order is important.
And async is used for independent scripts, like counters or ads. And their relative execution order does not matter.
Please note: if you’re using defer or async , then user will see the page before the script loads.
In such case, some graphical components are probably not initialized yet.
Don’t forget to put “loading” indication and disable buttons that aren’t functional yet. Let the user clearly see what he can do on the page, and what’s still getting ready.
Знакомство с JavaScript. Подключение к странице

JavaScript (сокращённо JS) – это язык программирования , который изначально был придуман для браузера, чтобы придать страницам интерактивность и динамичность .
Программы написанные на JavaScript называются сценариями или скриптами . Добавление их на страницу выполняется через тег <script> . При этом их можно как непосредственно вставлять на веб-страницу, так и размещать в отдельном файле. Каждый браузер имеет интерпретатор JavaScript, с помощью которого он выполняет этот код.
Потребность в создании языка программирования для веб-браузера возникла в 90-е годы. В это время на веб-страницах хотелось делать уже намного больше, чем просто выводить статичный контент.
Данный язык изначально был создан Бренданом Айком в 1995 году для браузера Netscape и назывался он Mocha. Но после ряда переименований он обрёл окончательное имя – JavaScript . В начале он имел очень маленькую функциональность и в основном использовался для добавления на страницу интерактивности. Но со временем язык стал развиваться и позволять делать всё больше и больше.
В 1996 году язык JavaScript был стандартизован компанией Ecma, которая занимается стандартизацией информационных и коммуникационных технологий. Сама спецификация была названа ECMAScript или сокращённо ES. По сути, JavaScript является реализацией спецификации ECMAScript. Новые версии ECMAScript выходят ежегодно и добавляют в язык новые возможности.
- ECMAScript 1 – июнь 1997;
- ECMAScript 2 – июнь 1998;
- ECMAScript 3 – декабрь 1999;
- ECMAScript 5 – декабрь 2009;
- ECMAScript 5.1 – июнь 2011;
- ECMAScript 2015 (ES2015, ECMAScript 6, ES6) – июнь 2015;
- ECMAScript 2016 (ES2016, ECMAScript 7, ES7) – июнь 2016;
- ECMAScript 2017 (ES2017, ECMAScript 8, ES8) – июнь 2017;
- ECMAScript 2018 (ES2018, ECMAScript 9, ES9) – июнь 2018;
- ECMAScript 2019 (ES2019, ECMAScript 10, ES10) – июнь 2019;
- ECMAScript 2020 (ES2020, ECMAScript 11, ES11) – июнь 2020;
- ECMAScript 2021 (ES2021, ECMAScript 12, ES12) – июнь 2021.
ECMAScript – это стандарт JavaScript, который описывает полностью все функции JavaScript. Браузеры, Node.js применяют это стандарт и реализуют его поддержку.
Начиная с версии ES6 язык значительно преобразился, в нём появился новый синтаксис для написания сложных приложений (классы, модули, итераторы, циклы, генераторы в стиле Python, стрелочные функции, ключевые слова let , const и многое другое). С выходом версии ES6 весь код JavaScript принято делить на modern (современный) и классический (до ES6) .
В настоящее время JavaScript применяется не только в веб-браузере. С помощью него можно писать десктопные и мобильные приложения, использовать его на сервере (Node.js).
Язык JavaScript, как и другие языки программирования, имеет некоторые свои особенности. Среди основных – слабая типизация и динамическое приведение типов .
JavaScript – это не Java , хоть он и унаследовал некоторые синтаксические конструкции этого языка. Такое название данный язык получил в силу некоторых исторических причин. Одной из них являлось то, что изначально в качестве языка, который должен был быть доступным в браузере, хотели сделать Java . Но впоследствии компания Netscape отказалась от этой мысли, из-за того, что Java был слишком большим и сложным.
Виды браузеров и браузерных движков
В настоящее время существует большое количество браузеров . Любой современный браузер основывается на движке . Движок – это часть браузера, которая преобразует HTML, CSS, JavaScript, изображения и другую информацию в интерактивную картинку .
Основные современные движки и браузеры, которые их используют:
- Blink (Google Chrome, Opera, Яндекс.Браузер и др.);
- Gecko (Mozilla Firefox, Waterfox и др);
- WebKit (Safari, Maxthon, Vivaldi и др.);
- EdgeHTML (Microsoft Edge).
Подключение JavaScript к странице
Добавление JavaScript на страницу выполняется с помощью тега <script> .
Первый способ – это вставка кода непосредственно на страницу . Выполняется это между открывающим и закрывающим тегом <script> .
Второй способ заключается в использовании отдельного файла с расширением js . В данный файл необходимо поместить код JavaScript, а затем подключить его к странице с помощью <script> . Путь к файлу задаётся с помощью атрибута src .
С помощью этого способа можно подключить JavaScript к большому количеству HTML страниц. Это позволяет при изменении кода не править его на каждой странице.
<script> можно поместить внутрь любого элемента, но рекомендуется непосредственно в <head> или <body> :
Если подключить скрипт с помощью атрибута src и дополнительно ещё указать некоторый код между открывающим и закрывающим тегом script , то код, который вы указали непосредственно, будет проигнорирован, т.е. он не выполнится.
Как выполняются скрипты на странице?
Когда браузер читает страницу и встречает на ней script , он останавливает дальнейшую загрузку страницы и выполняет, подключенный с помощью этого элемента JavaScript. После чего приступает к дальнейшей загрузке страницы:
В этом примере используются 2 метода:
- alert() – для вывода на экран предупреждения с кнопкой «ОК»; данное предупреждение приостанавливает дальнейшую загрузку страницу;
- document.write() – для вывода текста в текущее место HTML, если страница ещё не загрузилась.
При загрузке этого документа, когда браузер встретит alert() , он выведет на экран предупреждение «HEAD: Hello, world!». Кроме этого, он приостановит дальнейшую загрузку страницы , пока пользователь не нажмёт на кнопку «ОК».
На этом этапе загрузки страницы:

После нажатия на кнопку «ОК» браузер продолжит загрузку страницы. Он выполнит метод document.write() , который выведет значение, указанное в скобках сразу после открывающего тега <body> . Затем тег <h1> и в завершении выполнит метод document.write() , который выведет текст «BODY: Hello, world!».

async и defer
До появления этих атрибутов у нас не было возможности загружать JavaScript в фоне. Поэтому многие сайты, в которых JavaScript не использовался для формирования первоначальной структуры страницы, подключали его в самом конце, т.е. перед закрывающим тегом <body> . В основном это использовалось для того, чтобы пользователь мог как можно быстрее увидеть страницу и начать с ней взаимодействовать. А так как такой код взаимодействует с уже загруженной страницей, его в принципе и вставлять выше не имеет смысла.
async – атрибут для <script> , который используется для того, чтобы указать браузеру на то, что этот скрипт необходимо загрузить асинхронно, т.е. не останавливая основной поток чтения страницы. После загрузка скрипта, браузер сразу же его выполнит.
defer – это ещё один атрибут для <script> , похожий на async . Он также указывает, что скрипт необходимо загрузить в фоне. Но в отличие от async , он будет выполнен после загрузки страницы, а точнее DOM. Кроме этого, стоит отметить, что скрипты, заданные с атрибутом async , выполняются перед вызовом события DOMContentLoaded .
Ещё одно отличие defer от async в том, что defer сохраняет очередность их выполнения:
Т.е. вне зависимости от того какой скрипт загрузится быстрее, они всё равно будут выполнены браузером в том порядке, в котором они расположены в коде. В данном примере, сначала выполнится «script-1.js», а затем «script-2.js» даже если второй загрузится быстрее, чем первый.
Если для <script> одновременно указать два атрибута, т.е. сразу async и defer , то будет работать только async .
Атрибуты async и defer можно использовать только для скриптов, подключаемых на страницу с использованием src .
Name already in use
ru.javascript.info / 2-ui / 5-loading / 02-script-async-defer / article.md
- Go to file T
- Go to line L
- Copy path
- Copy permalink
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
Скрипты: async, defer
В современных сайтах скрипты обычно «тяжелее», чем HTML: они весят больше, дольше обрабатываются.
Когда браузер загружает HTML и доходит до тега <script>. </script> , он не может продолжать строить DOM. Он должен сначала выполнить скрипт. То же самое происходит и с внешними скриптами <script src=»https://github.com/javascript-tutorial/ru.javascript.info/blob/master/2-ui/5-loading/02-script-async-defer/»></script> : браузер должен подождать, пока загрузится скрипт, выполнить его, и только затем обработать остальную страницу.
Это ведёт к двум важным проблемам:
- Скрипты не видят DOM-элементы ниже себя, поэтому к ним нельзя добавить обработчики и т.д.
- Если вверху страницы объёмный скрипт, он «блокирует» страницу. Пользователи не видят содержимое страницы, пока он не загрузится и не запустится:
Конечно, есть пути, как это обойти. Например, мы можем поместить скрипт внизу страницы. Тогда он сможет видеть элементы над ним и не будет препятствовать отображению содержимого страницы:
Но это решение далеко от идеального. Например, браузер замечает скрипт (и может начать загружать его) только после того, как он полностью загрузил HTML-документ. В случае с длинными HTML-страницами это может создать заметную задержку.
Такие вещи незаметны людям, у кого очень быстрое соединение, но много кто в мире имеет медленное подключение к интернету или использует не такой хороший мобильный интернет.
К счастью, есть два атрибута тега <script> , которые решают нашу проблему: defer и async .
Атрибут defer сообщает браузеру, что он должен продолжать обрабатывать страницу и загружать скрипт в фоновом режиме, а затем запустить этот скрипт, когда DOM дерево будет полностью построено.
Вот тот же пример, что и выше, но с defer :
- Скрипты с defer никогда не блокируют страницу.
- Скрипты с defer всегда выполняются, когда дерево DOM готово, но до события DOMContentLoaded .
Следующий пример это показывает:
- Содержимое страницы отобразится мгновенно.
- Событие DOMContentLoaded подождёт отложенный скрипт. Оно будет сгенерировано, только когда скрипт (2) будет загружен и выполнен.
Отложенные с помощью defer скрипты сохраняют порядок относительно друг друга, как и обычные скрипты.
Поэтому, если сначала загружается большой скрипт, а затем меньшего размера, то последний будет ждать.
«`smart header Атрибут `defer` будет проигнорирован, если в теге `<script>` нет `src`.
- Содержимое страницы отображается сразу же : async его не блокирует.
- DOMContentLoaded может произойти как до, так и после async , никаких гарантий нет.
- Асинхронные скрипты не ждут друг друга. Меньший скрипт small.js идёт вторым, но скорее всего загрузится раньше long.js , поэтому и запустится первым. То есть, скрипты выполняются в порядке загрузки.
Асинхронные скрипты очень полезны для добавления на страницу сторонних скриптов: счётчиков, рекламы и т.д. Они не зависят от наших скриптов, и мы тоже не должны ждать их:
Динамически загружаемые скрипты
Мы можем также добавить скрипт и динамически, с помощью JavaScript:
Скрипт начнёт загружаться, как только он будет добавлен в документ (*) .
Динамически загружаемые скрипты по умолчанию ведут себя как «async».
- Они никого не ждут, и их никто не ждёт.
- Скрипт, который загружается первым — запускается первым (в порядке загрузки).
Мы можем изменить относительный порядок скриптов с «первый загрузился — первый выполнился» на порядок, в котором они идут в документе (как в обычных скриптах) с помощью явной установки свойства async в false :
Например, здесь мы добавляем два скрипта. Без script.async=false они запускались бы в порядке загрузки ( small.js скорее всего запустился бы раньше). Но с этим флагом порядок будет как в документе:
У async и defer есть кое-что общее: они не блокируют отрисовку страницы. Так что пользователь может просмотреть содержимое страницы и ознакомиться с ней сразу же.
Но есть и значимые различия:
Порядок DOMContentLoaded async Порядок загрузки (кто загрузится первым, тот и сработает). Не имеет значения. Может загрузиться и выполниться до того, как страница полностью загрузится. Такое случается, если скрипты маленькие или хранятся в кеше, а документ достаточно большой. defer Порядок документа (как расположены в документе). Выполняется после того, как документ загружен и обработан (ждёт), непосредственно перед DOMContentLoaded . На практике defer используется для скриптов, которым требуется доступ ко всему DOM и/или важен их относительный порядок выполнения.
А async хорош для независимых скриптов, например счётчиков и рекламы, относительный порядок выполнения которых не играет роли.