Как взаимодействуют Асинхронные Javascript-программы

В первые дни разработки веб-сайтов я подхватил немного народной мудрости, которая для кода вроде этого

<script src=".../program1.js"></script>
<script src=".../program2.js"></script>

браузер будет останавливаться, загружать javascript, компилировать его, выполнять, переходить к следующему тегу script и повторять. Таким образом, браузер будет работать через весь javascript на странице и рассматривать его как одну линейную программу.

Однако в храбром новом современном javascript мире мы имеем асинхронную загрузку через атрибут async

<script src=".../program1.js" async></script>
<script src=".../program2.js" async></script>

Я понимаю, что это хорошо, потому что теперь браузеру не нужно останавливаться, загружать script и выполнять его. Вместо этого он начинает загрузку script, но будет продолжать разбирать DOM. то есть веб-страница больше не блокируется, ожидая загрузки javascript. (если это не так, я буду благодарен за исправление).

Однако, что менее ясно (и сложнее проверить), как эти две программы взаимодействуют. Кажется, что они работают в одном и том же разделяемом пространстве (т.е. Javascript по-прежнему, с точки зрения пользователя, однопоточен с двумя (глобальными, функциями) областями). Тем не менее, какой порядок они выполняют, в документации, которую я прочитал, явно не определен.

Я прочитал статью MDN в Concurrency model и Event Loop. Хотя он интересен и полезен, он не совсем отвечает на мой вопрос. Из того, что я собираю, когда браузер загружает program1.js или program2.js, javascript добавит сообщение в очередь событий, и это сообщение будет обработано, поскольку механизм javascript запускается через цикл событий.

Что мне не хватает - что говорит это сообщение? Это одно сообщение для каждой программы, которое говорит "компилировать и выполнять весь этот код javascript"? Или каждая программа создает несколько сообщений - на мой взгляд, которые могут выглядеть примерно как

  • Сообщение 1: Извлеките все функции из этой программы и скомпилируйте их
  • Сообщение 2: Извлеките из этой программы все операторы и выражения в глобальной области.
  • Сообщение 3-n: Добавить каждую инструкцию и выражение как отдельное сообщение очереди для последующей обработки.

И что происходит, когда браузер находится в середине обработки program1.js, но заканчивает загрузку program2.js? Возможно ли, что выполнение инструкций из каждой программы будет чередоваться?

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

Ответ 1

Есть две статьи, иллюстрирующие свойства "асинхронные" и "отсроченные" в практическом смысле (я зеленый на внутренних страницах браузера):

С 2014 года с отличной + простой графикой: http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html

С 2016 года почему async может быть анти-шаблоном: http://calendar.perfplanet.com/2016/prefer-defer-over-async/

Асинхронный

На ваш вопрос, что "асинхронно" указывает браузеру:

  • Начните загрузку этого ресурса как можно раньше.
  • Переходите и не стесняйтесь загружать другие ресурсы параллельно.
  • Но как только моя загрузка завершится, прекратите рендеринг и начните выполнять меня как можно раньше.

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

Несколько асинхронных скриптов не гарантируют, какой заказ они будут выполнять. Зависит от скорости загрузки. Это позволяет AMD-системам, таким как RequireJS, определять зависимости и обратные вызовы для загрузки ресурсов async, но очереди их выполнения до тех пор, пока "глобальная" среда не будет содержать все предварительные условия, и порядок выполнения может быть согласован (по манере рун), надеюсь.

Перенести

Defer ведет себя так:

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

С одной стороны, "дефер" быстрее, потому что он никогда не может блокировать парсер или рендеринг. Но "отложить" может быть медленнее, потому что он должен ждать выполнения, пока конвейер не станет ясным.

Звучит так: "async" всегда лучше, но если вы загружаете 2 МБ JS на слабом процессоре с быстрым подключением, вы можете закончить ожидание 10 секунд до того, как парсеру разрешено завершить рендеринг. Использование "defer" предотвратит это за счет задержки вашего уровня взаимодействия.

Различие более бесполезно, если вы говорите о клиентах или серверах или приложениях на стороне клиента. Возможно, более выгодно использовать отсрочку в backend-тяжелом приложении, таком как Magento, где рендеринг обрабатывается на стороне сервера.

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

Ответ 2

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

Нет, абсолютно нет. JS по-прежнему однопоточен, и одна программа запускается за другой (хотя какая из них на первом месте может быть неизвестна).

Что сообщает сообщение цикла событий?

Сообщение ScriptEvaluationJob. Предполагая, что синтаксический анализ script будет успешным, он будет создавать все объявления и оценивать тело script, все за один проход.