Как работает цикл событий в JavaScript?

Как работает цикл событий в JavaScript?

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

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

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

Эта статья посвящена одной из очень важных, но редко понимаемых концепций или терминов в JavaScript. ЦИКЛ СОБЫТИЙ!.

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

Прежде чем мы поймем, как работает цикл событий, нам сначала нужно понять, что такое сам JavaScript и как он работает!

Что такое JavaScript?

Прежде чем мы продолжим, я хотел бы вернуться к самым основам. Что такое JavaScript? Мы могли бы определить JavaScript как;

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

Подождите, что это? Определение книги? ?

Давайте разбить его!

Ключевые слова в отношении этой статьи: однопоточный, неблокирующий, параллельный и асинхронный.

Один поток

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

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

Как JavaScript может быть однопоточным и неблокирующим одновременно?

Но что означает блокировка?

Неблокирующий

Не существует единого определения блокировки; это просто означает то, что медленно работает в потоке. Таким образом, неблокирующий означает, что поток не замедляется.

Но подождите, я сказал, что JavaScript работает в одном потоке? Я также сказал, что это не блокирует, что означает, что задача выполняется быстро в стеке вызовов? Но как??? Как насчет того, когда мы запустим таймеры? Петли?

Расслабляться! Узнаем чуть позже ?.

В то же время

Параллелизм означает, что код выполняется одновременно более чем одним потоком.

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

Асинхронный

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

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

Прежде чем мы продолжим, давайте резюмируем вышесказанное.

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

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

Давайте немного углубимся, перейдем к механизму выполнения JavaScript, V8, возможно, есть какие-то скрытые потоки, о которых мы не знаем.

двигатель V8

Движок V8 — это высокопроизводительный механизм веб-сборки JavaScript с открытым исходным кодом, написанный Google на C++. Большинство браузеров запускают JavaScript с использованием движка V8, и даже популярная среда node js использует его.

Говоря простым языком, V8 — это программа на C++, которая берет код JavaScript, компилирует его и выполняет.

V8 делает две основные вещи;

  • Распределение кучи памяти
  • Контекст выполнения стека вызовов

К сожалению, наше подозрение было ошибочным. V8 имеет только один стек вызовов, думайте о стеке вызовов как о потоке.

Один поток === один стек вызовов === одно выполнение за раз.

Изображение — Hacker Noon

Поскольку V8 имеет только один стек вызовов, как JavaScript работает параллельно и асинхронно, не блокируя основной поток выполнения?

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

JavaScript выполняет каждый код построчно, один за другим (однопоточный). Как и ожидалось, первая строка здесь выводится на консоль, но почему последняя строка выводится до истечения срока действия кода? Почему процесс выполнения не ждет кода тайм-аута (блокировки), прежде чем продолжить выполнение последней строки?

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

Давайте ненадолго взглянем на исходный код V8.

Чего ждать??!!! В V8 нет функций таймера, нет DOM? Нет событий? Нет АЯКСА?…. Дааааааааааааааааааааа!!!

События, DOM, таймеры и т. д. не являются частью основной реализации JavaScript, JavaScript строго соответствует Спецификации сценариев Ecma, и на его различные версии часто ссылаются в соответствии со спецификацией сценариев Ecma (ES Xs).

Рабочий процесс выполнения

События, таймеры, запросы Ajax предоставляются браузером на стороне клиента и часто называются веб-API. Именно они делают однопоточный JavaScript неблокирующим, параллельным и асинхронным! Но как?

В рабочем процессе выполнения любой программы JavaScript есть три основных раздела: стек вызовов, веб-API и очередь задач.

Стек вызовов

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

Давайте рассмотрим следующий пример;

Источник — https://ioutu.be/8aGhZKkoFbK

Когда вы вызываете функцию printSquare(), она помещается в стек вызовов, функция printSquare() вызывает функцию Square(). Функция Square() помещается в стек, а также вызывает функциюmulti(). Функция умножения помещается в стек. Поскольку функция умножения возвращается и является последней вещью, помещаемой в стек, она сначала разрешается и удаляется из стека, за ней следует функция Square(), а затем функция printSquare().

Веб-API

Здесь выполняется код, который не обрабатывается движком V8, чтобы не «блокировать» основной поток выполнения. Когда стек вызовов встречает функцию веб-API, процесс немедленно передается веб-API, где он выполняется и освобождает стек вызовов для выполнения других операций во время его выполнения.

Вернемся к нашему примеру setTimeout выше;

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

Пока тайм-аут все еще работает, стек переходит к следующей строке действий и запускает последний console.log, что объясняет, почему мы получаем это до выхода таймера. Когда таймер заканчивается, что-то происходит. Таймер console.log волшебным образом появляется в пуле вызовов!

Как?

Цикл событий

Прежде чем мы обсудим цикл событий, давайте сначала рассмотрим функцию очереди задач.

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

Очередь — это структура данных, которая работает по принципу «первым поступил — первым обслужен», поэтому, когда задачи помещаются в очередь, они выходят в том же порядке. Задачи, выполняемые веб-API, которые помещаются в очередь задач, а затем возвращаются в стек вызовов для печати результата.

Но ждать. ЧТО ТАКОЕ ЦИКЛ СОБЫТИЙ???

Источник — https://ioutu.be/8aGhZKkoFbK

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

Источник — https://www.kuora.com/Hov-does-an-event-loop-vork/answer/Timothy-Macwell.

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

Заключение

Хотя это очень простое введение, концепция асинхронного программирования в JavaScript дает достаточно информации, чтобы ясно понять, что происходит под капотом и как JavaScript может работать одновременно и асинхронно только с одним потоком.

JavaScript всегда востребован, и если вам интересно его изучить, я бы посоветовал вам пройти этот курс Udemy.

Поделиться в соцсетях