Глубокое погружение во внутренности Javascript
Внутренняя работа механизма Javascript, цикла событий, веб-API, очередей событий и обратных вызовов.
Введение
Вы когда-нибудь представляли себе, что было бы, если бы не было Javascript? Без Javascript вы не сможете транслировать фильмы на Netflix, делать покупки на Amazon или смотреть видео на Youtube. Почти каждый второй веб-сайт использует Javascript и делает веб-страницу интерактивной.
До того, как был изобретен Javascript, Интернет состоял только из простых HTML- страниц. HTML и CSS придают веб-странице структуру и стиль. Но Javascript делает веб-страницу динамической . Вы можете вводить свои учетные данные и авторизоваться на веб-сайте, выполнять онлайн-транзакции или играть в игры. Javascript делает все возможное.
Один из опросов, проведенных на Stack-overflow, показал, что Javascript имеет наибольшее количество репозиториев на GitHub. Каждый год на Javascript разрабатываются новые фреймворки и библиотеки. Язык действительно изменил Интернет и упростил жизнь людей.
Фон
Два года назад я работал над сайд-проектом и впервые использовал Javascript. Будучи опытным программистом с прошлого десятилетия, я хорошо разобрался в синтаксисе и за три месяца наработал хорошую команду.
Шли месяцы, веб-страница, которую я разработал, не работала должным образом. Загрузка заняла много времени, и это начало влиять на клиентов. Итак, я сделал глубокое погружение, чтобы определить основную причину.
Проведя бессонные ночи, я окончательно потерял рассудок и был готов сдаться. Однако я решил сделать последнюю попытку. Наконец, я подумал о том, чтобы получить полное представление о том, как работает Javascript внутри. Я ссылался на множество ресурсов и, наконец, смог исправить ошибку в своем коде за два дня.
Инженеры, как правило, сосредоточены на создании новых вещей. Инструменты и фреймворки, которые мы используем, скрывают от нас большую часть сложности. В нашем путешествии мы склонны забывать, как на самом деле все работает за кулисами. Необходимо знать внутреннюю работу любого инструмента, который мы используем. Хорошее понимание внутренностей помогает нам сузить проблему и найти прагматичное решение.
В этой статье мы рассмотрим, что происходит за кулисами, когда Javascript выполняется браузером. Мы рассмотрим движок Javascript и поймем, как он работает. Мы увидим различные компоненты, участвующие в среде выполнения Javascript, и получим ясность в цикле событий и очереди событий. Хотя эти термины могут быть для вас новыми, но в статье мы попытаемся упростить каждый из них. Давайте начнем с нашего стремления освоить внутренности Javascript.
Javascript
Javascript — интерпретируемый язык высокого уровня. В отличие от C или C++, нам не нужно компилировать код Javascript, а затем запускать его. Код Javascript сначала компилируется в байт-код, а затем транслируется в код машинного уровня.
Высокотехнологичные языки, такие как Java, C, C++, C#, поддерживают многопоточность. Эти языки позволяют программистам создавать несколько потоков и выполнять задачи в разных потоках. Таким образом, мы можем создавать задачи на нескольких процессорах и эффективно использовать вычислительные ресурсы.
Напротив, Javascript является однопоточным. Это означает, что в любой момент времени существует только один поток выполнения. В случае, если вы выполните функцию javascript, требующую времени, веб-страница зависнет. Возможно, вы много раз видели ошибку «Веб-сайт не отвечает» во время просмотра.
Давайте сделаем быструю демонстрацию и посмотрим на работу в действии. Я открыл Twitter и могу нажимать на отдельные вкладки, такие как Trending, Newsи Sportsт. д.
Теперь в своей developerконсоли я пишу функцию, содержащую бесконечный цикл. Ниже приведено определение функции.
function infiniteLoop() {
for(i=0; i < 100000; ) {
}
}
infiniteLoop()
Ниже GIF показано, что я не могу изменить вкладку на Sportsили Entertaintment.
Поскольку функция содержит бесконечный цикл, она будет выполняться бесконечно. Когда я выбираю разные вкладки, браузер должен обрабатывать событие clickи соответственно отображать содержимое выбранной вкладки. Я не могу выбрать какую-либо другую вкладку, так как Javascript уже выполняет эту функцию infiniteLoop.
Как видно из приведенного выше примера, мы можем запускать только одну функцию за раз в Javascript. Теперь вы можете задаться вопросом, как браузер может загружать сложные веб-сайты за доли секунд.
В последующих разделах мы поймем, как Javascript делает эту магию за кулисами. Давайте теперь разберемся с ядром Javascript, т.е. Javascript Engine.
Javascript-движок
Каждый браузер предоставляет свой собственный Javascript Engine. Например: - Chrome использует движок V8, а Firefox использует движок SpiderMonkey. Engine отвечает за выполнение кода Javascript построчно. Ниже приведен список популярных браузеров и их движков Javascript.
Движок Javascript состоит из кучи и стека . Давайте разберемся в работе этих двух элементов.
куча
Куча отвечает за хранение определений функций, объектных переменных . Он содержит все объекты, на которые ссылается код.
Объекты создаются динамически в куче. Работа похожа на java. Всякий раз, когда на объект ссылается функция, он извлекается из кучи.
function bar() {
var player = { "firstName": "Shubhman",
"lastName": "Gill"
}
var teams = ["India", "NewZealand"]
i =0
a = 10
console.log("In the function bar")
}
bar()
Objects on Heap
Куча
Стек — это структура данных, работающая по принципу «последним пришел — первым обслужен» (LIFO) . Каждый раз, когда функция запускается, локальные переменные функции помещаются в стек. Как только функция завершает выполнение, она удаляется из стека. Если функция вызывает новую функцию во время выполнения, новая функция также помещается в стек. Engine запускает функцию, которая находится на вершине стека.
Функции, находящиеся в нижней части стека, возобновляют свое выполнение после возврата всех функций, находящихся выше. В случае, если функция выдает ошибку, трассировка стека печатается в файле console. Трассировка стека состоит из цепочки функций, которые вызывались при возникновении исключения. Давайте посмотрим на пример.
Я собираюсь запустить приведенный выше код в своем браузере. И добавьте точку останова в функцию city.
Как видно выше, при выполнении сценария он остановился в точке останова в функции city. С правой стороны, в Call Stackразделе, вы можете увидеть cityфункцию вверху.
Состоит Call Stackиз всех функций, вызванных до сих пор. И самая последняя функция помещается вверху. Как только функция завершает выполнение, она удаляется из стека.
На изображении ниже вы можете видеть, что функция cityудалена, а выполнение функции продолжается streetв строке номер 9.
Как видно выше, Javascript может выполнять только одну функцию за раз, и существует только один поток выполнения. Вы не можете одновременно запускать несколько функций в стеке выполнения.
Допустим, у нас есть функция, которая выполняет вызов HTTPи получает данные с другого сайта. Что произойдет, если сервер будет работать медленно, а выполнение функции займет много времени? Мы не сможем запустить какую-либо другую функцию, и пользовательский опыт будет очень плохим.
Это происходит в реальном мире? Нет, сложные веб-сайты, такие как Facebook.com , Twitter.com , Amazon.com и т. д., используют тонны кода Javascript, не влияя на работу пользователей. Чтобы понять, как это сделать, мы углубимся в среду выполнения Javascript.
Среда выполнения Javascript
Javascript предоставляет нам различные API для получения данных с сервера, управления DOM или выполнения функций с использованием таймера. Из-за однопоточной природы Javascript он может остановить рендеринг веб-сайта, что приведет к снижению производительности. Однако среда выполнения Javascript гарантирует, что этого не произойдет. Давайте разберемся с этим на примере.
Вы можете догадаться, что будет на выходе кода ниже: -
Будет ли getSubTitlesнапечатано после печати getActors? И после звонка setTimeoutкод остановится?
Ниже приведен вывод, полученный при выполнении вышеуказанного кода:
Как видно выше, getSubTitlesсначала было напечатано, а через 4 секунды getActorsбыло напечатано. Хотя функция setTimeoutждала 4 секунды, но все равно выполняла последующую функцию getSubTitlesраньше, а код не ждал. Давайте теперь разберемся, что происходило за кулисами.
Веб-API
Вы не найдете реализации таких функций, как setTimeout, или fetchнигде в исходном коде Javascript. Эти функции предоставляются браузером и не являются частью Javascript. Реализация будет отличаться для каждого браузера.
Браузер предоставляет функции для выполнения сетевых вызовов, управления DOM, обработки аудио, видео и т. д. Эти функции образуют веб-API. Внутренняя реализация сложна и в основном состоит из кода C++. API-интерфейсы предоставляют нам интерфейс, который можно вызывать из кода Javascript.
Когда вы вызываете такую функцию, как setTimeout, Javascript понимает, что это веб-API. Затем он делегирует выполнение этой функции контейнеру веб-API. Функция setTimeoutвыполняется в отдельном потоке. Он не выполняется в потоке выполнения Javascript. По этой причине код, написанный после, setTimeoutвыполняется до того, как ему будет передана функция обратного вызова.
Точно так же, поскольку сетевые вызовы занимают много времени, такие функции, как fetchвыполнение, выполняются в отдельном потоке. Веб-API браузера скрывают от нас большую часть сложностей. Например: внутренняя реализация fetchбудет содержать формирование TCP-соединения, отправку данных и их анализ. Кроме того, он также будет эффективно использовать вычислительные ресурсы, планируя выполнение в соответствующем потоке.
На приведенном ниже gif-файле показано, что происходит, когда вы выполняете такие функции веб-API, как setTimeout.
Как показано выше, после setTimeoutвызова функции она отправляет функцию обратного вызова getActorsв веб-API. В контейнере веб-API функция setTimeoutожидает четыре секунды. Тем временем getSubTitlesвызывается следующая функция.
Теперь вы можете спросить, как функция обратного вызова getActorsвыполняется после окончания таймера. В следующем разделе мы увидим, как выполняются обратные вызовы после возврата веб-API.
Очередь событий
Javascript сильно зависит от функций обратного вызова. Функции обратного вызова выполняются при возникновении события. Например: - После получения ответа от сервера нам нужно распечатать представление json. Или выполнить функцию после задержки в 4 секунды.
Среда выполнения Javascript состоит из компонента, известного как очередь событий или очередь обратного вызова. Это очередь сообщений, в которой используется порядок «первым пришел – первым обслужен» (FIFO). Сообщения представляют собой не что иное, как функции обратного вызова, запланированные для выполнения.
Давайте возьмем пример, чтобы понять это. В приведенном ниже коде мы регистрируем функцию обратного вызова всякий раз, когда происходит событие нажатия на кнопку. Как только пользователь нажмет кнопку, функция onClickбудет вызвана. Эта функция вызывает функцию timerпосле задержки в 2 секунды.
Анимация выше показывает, что происходит за кулисами. Здесь у нас есть два веб-API — а) $.onи б) setTimeout. Первый API ожидает, когда произойдет событие щелчка. Как только он получит событие click, он выполнит функцию onClick. Второй API setTimeOutвпоследствии вызывается функцией onClick.
Каждый раз, когда пользователь нажимает кнопку, функция onClickпомещается в очередь событий. Точно так же после окончания таймера функция timerпомещается в очередь. Таким образом, очередь событий состоит из всех функций обратного вызова, которые должны быть выполнены. В данном случае onClickи timerфункция.
Цикл событий
Функции обратного вызова EventQueueдостигают стека выполнения Javascript через цикл обработки событий. Цикл событий постоянно проверяет, пуст ли текущий стек Javascript. Если он обнаруживает, что стек пуст, он выбирает первое сообщение из очереди событий и помещает его в стек.
Взяв пример из предыдущего раздела, как только цикл обработки событий обнаружит, что стек пуст, он возьмет следующее сообщение из очереди обратного вызова и выполнит его.
Ниже приведен грубый псевдокод для работы цикла событий:
Вы можете думать о цикле событий как о бесконечном цикле, который постоянно отслеживает стек выполнения. Если стек пуст, он возьмет сообщение (функцию) из очереди событий и выполнит его.
На приведенной ниже диаграмме показано высокоуровневое представление среды выполнения Javascript.
Мы рассмотрели различные компоненты среды выполнения Javascript. Теперь у вас будет лучшее представление о том, как Javascript работает внутри. Давайте обобщим то, что мы узнали на данный момент.
Краткое содержание
- Javascript является однопоточным и выполняет одну функцию за раз.
- Он использует веб-API для выполнения сложных задач в фоновом режиме.
- После завершения выполнения веб-API функции обратного вызова отправляются в очередь событий.
- Цикл событий постоянно проверяет, пуст ли стек Javascript. Если стек пуст, он выбирает функцию обратного вызова из очереди событий и запускает ее.
- Внутреннее устройство и среда выполнения Javascript
- Как работает Javascript в браузере и узле
- Анимированное руководство по циклу событий Node.js
- Что, черт возьми, такое цикл событий?
- Асинхронный Javascript и цикл событий с нуля

![В любом случае, что такое связанный список? [Часть 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































