Обзор механизма JavaScript, цикла событий, очереди обратного вызова и веб-API.

JavaScript - это высокоуровневый, однопоточный, собирающий мусор, интерпретируемый или точно скомпилированный, основанный на прототипах, многопарадигмальный, динамический язык с неблокирующим событием loop.¹

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

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

Здесь мы рассмотрим различные компоненты, которые задействованы при запуске кода JavaScript.

Движок JavaScript

Механизм JavaScript - это та часть, где выполняется код.

Вот список некоторых популярных движков JavaScript:

  • V8 от Google используется в браузере Google Chrome и в Node.js.
  • SpiderMonkey от Mozilla, используемый в браузере Mozilla Firefox.
  • Nitro и JavaScriptCore от Apple, используемые в браузере Safari.
  • Chakra и CharkraCore от Microsoft, используемые в браузере Microsoft Edge.

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

Двумя основными компонентами движка являются кучная память (выделение памяти) и стек вызовов (контекст выполнения).

Куча памяти

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

Стек вызовов

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

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

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

Пример, показывающий простую синхронную задачу вместе с состоянием стека вызовов во время выполнения:

Пример, показывающий простую синхронную задачу, генерирующую ошибку вместе с состоянием стека вызовов на протяжении всего выполнения:

Среда выполнения

Средой выполнения для JavaScript может быть браузер или среда выполнения Node.js, которую вы установили.

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

Среда выполнения JavaScript (которая включает движок JavaScript) предоставляет некоторые компоненты, такие как веб-API, очередь обратных вызовов и цикл событий, чтобы сделать JavaScript параллельным. Обсудим подробно каждый из них.

Веб-API

Большинство асинхронных задач, для выполнения которых может потребоваться некоторое время, будут помещены в стек вызовов, а затем немедленно переданы в часть веб-API среды выполнения.

Некоторые из предоставляемых браузером веб-API с асинхронным поведением - это DOM API, Timer (setTimeout и setInterval), Fetch API, а синхронные - это Storage API (sessionStorage и localStorage).

Вы можете ознакомиться со всеми доступными веб-API здесь.

Очередь обратного вызова (очередь задач)

Все обратные вызовы и результаты различных веб-API будут помещены в обратный вызов или очередь задач. Все обратные вызовы / результаты будут поставлены в очередь, а затем будут перемещены в стек вызовов циклом событий.

Очередь обратного вызова для механизма V8 имеет очередь микрозадач и очередь макросов.

Обратные вызовы, обещания и вызовы асинхронных функций классифицируются как микрозадачи. Вышеупомянутые задачи имеют приоритет над такими вещами, как setTimeout, setInterval, DOM Events и т. Д., Которые считаются макро-задачами.

Результаты и обратные вызовы перемещаются в соответствующие очереди после обработки WebAPI.

Цикл событий

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

Цикл событий сначала помещает задачи из очереди микрозадач в стек вызовов, а затем просматривает очередь макрозадач. Этот процесс повторяется до тех пор, пока все элементы обратного вызова или очереди задач не будут помещены в стек и не будут выполнены.

Теперь давайте рассмотрим несколько примеров с различными веб-API, а также то, что выбирает цикл событий на основе микро- и макро-задач из очереди обратного вызова.

Пример, показывающий последовательность выполнения с установленным тайм-аутом вместе с обычными журналами консоли:

Пример, показывающий последовательность выполнения с установленным таймаутом, имеющим нулевое время, вместе с обычными журналами консоли:

В приведенном выше примере, даже если время ожидания для setTimeout() равно 0, оно передается в веб-API и немедленно перемещается в очередь обратного вызова и вызывается после распечатки всех журналов синхронной консоли.

Пример, показывающий несколько setTimeout с одним и тем же временем вместе с журналами консоли:

В приведенном выше примере три setTimeout() имеют то же 1000, что и время ожидания, и отправляются в веб-API, затем перемещаются в очередь обратного вызова в той же последовательности один за другим после 1000 и вызываются после того, как все журналы синхронной консоли будут напечатаны. из.

Пример с установленным тайм-аутом и асинхронной функцией, имеющей Fetch API вместе с журналами консоли:

Пример с событием DOM onClick, setTimeout и простыми журналами консоли:

Заключение

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

использованная литература

  • [1] JavaScript: как это сделано Джеффом Делани на YouTube.
  • JS Engine на wikipedia.org.
  • Что, черт возьми, за цикл событий? Филиппа Робертса на YouTube.

Спасибо за чтение и удачного обучения!

Больше контента на plainenglish.io