Найти тему

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

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

Браузер, как представление работы Процессов и Потоков

И так, что же такое “процесс” и “поток”? Если говорить коротко, то процесс – это выполняющаяся программа приложения, а поток – это некая жизнь внутри процесса, которая выполняет какую-либо часть программы.

Так, например, когда мы запускаем приложение, то создаётся процесс, он же может создать поток(и), чтобы помочь себе в работе. Операционная система в свою очередь предоставляет процессу определённую область памяти для работы, и всё что связано с работой этого процесса храниться в этой области, соответственно, когда мы закрываем приложение, то процесс также закрывается, что позволяет операционной системе освободить память. Также приложение может попросить операционную систему выделить дополнительный процесс для корректной работы, в таком случае, выделяется отдельная область памяти, в которой уже разворачивается дополнительный процесс. Чаще всего, двум этим процессам необходимо общаться, они и могут это сделать с помощью межпроцессорного взаимодействия, другими словами IPC(Internet Process Communication). Большинство приложений разработано таким образом, что если один процесс не работает, то его можно перезапустить без закрытия остальных, выполняющих разные части приложения.

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

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

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

К примеру, рассмотрим процессы и потоки браузера Chrome:

  • Browser-Process – основной процесс браузера, он отвечает за координацию пользователя по интерфейсу, отображение элементов управления, например, таких как поисковая строка, кнопки “назад” и “вперёд” и т.д. Соответственно имеет такие потоки, как UI-поток, Network-поток и Storage-поток, отвечающие также за интернет соединение и доступ к файлам
  • Render-процесс – управляет всем, что находиться внутри одной вкладки. По умолчанию, каждая вкладка имеет один процесс, который к тому же является абсолютно независимым от других. Обычно этот процесс выполняет внутренние скрипты страницы, обрабатывает события и тому подобное, иными словами, максимально взаимодействует с JavaScript.
  • GPU-процесс – обрабатывает задачи GPU изолированно от других, так как обрабатывает запросы от нескольких приложений и рисует их на одной поверхности.

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

Волшебство превращения сухого кода в страницу

И так, мы уже знаем, что существует два основных похода к архитектуре браузера, также знаем, что многопоточный подход является более эффективным, что в браузере существует большое количество процессов, благодаря которым мы можем им пользоваться. В этой части статьи мы подробно поговорим о Render-процессе.

Главной задачей Render-процесса является преобразование HTML, CSS и JavaScript в веб-страницу. Главный поток процесса обрабатывает большую часть кода, который вы посылаете пользователю. Но как именно это работает? Сейчас разберёмся.

Как только браузер получает данные, он сразу начинает обрабатывать получаемую информацию, такая обработка носит название “Парсинг”.

Во время парсинга данные преобразуются в DOM. Что такое DOM? DOM или же Document Object Model– это внутреннее объектное представление разметки HTML. Браузер позволяет взаимодействовать с DOM используя различный инструментарий из JavaScript.Однако иметь DOM всё-таки недостаточно для отрисовки страницы, поэтому следующим этапом парсинга будет стилизация. Обычно наша страница не выглядит как сплошной статичный текст с картинками, собранный на голых HTML тегах. Для того, чтобы пользователю было приятно находиться на странице используют CSS для стилизации элементов. Главный поток разбивает CSS и определяет стиль для каждого узла DOM. На этапе синтаксического анализа формируются связи CSS селекторов и блоком правил этого селектора, такая структура называется CSSOM (CSS Object Model) – по аналогии с DOM. Уже после формирования DOM и CSSOM на их основе формируется дерево рендеринга иными словами набор объектов рендеринга. Rendering tree по сути своей дублирует DOM(не включая невидимые элементы) включая в свою структуру связь объекта DOM и объекта CSSOM. Как раз благодаря Rendering tree мы получаем описание визуального представления DOM. Далее предстоит тяжёлый пусть размещения (с помощью геометрии объектов и Layout tree) и отрисовки элементов (благодаря Layout tree и Painting records).

Впрочем, это достаточно обширная тема, подробно об этом рекомендую почитать в статье: https://web.dev/critical-rendering-path-render-tree-construction/

Так что там с потоками в JavaScript?

Отлично, теперь мы уже знаем, как происходит превращение кода в страницу. Самое время перейти к ключевой проблематике нашей темы. Вернёмся к потокам и процессам. Как мы знаем, JavaScript является полностью однопоточным, то есть один единственный поток обрабатывает цикл обработки событий, что сильно ограничивает наши возможности. В старых браузерах, весь браузер разделял один поток между всеми вкладками, понятное дело, что это порождало огромное количество проблем, хотя количество событий и скриптов на странице было небольшим, но всё-таки такое решение являлось очень проблематичным. В современных браузерах чаще всего используют принцип “на каждую вкладку – один поток”. Однако с этим тоже бывают проблемы, мы не можем позволить каждой вкладке обрабатывать несколько сценариев, запущенных одновременно.

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

И так, что такое web-workers? Это прежде всего поток, принадлежащий браузеру, который можно использовать для выполнения JavaScript кода без блокировки цикла событий. Как уже говорилось выше, JavaScript полностью однопоточный, а тут нам частично дают возможность обхода ограничений по циклу событий, нам больше не нужно использовать обходные пути, костыли, чтобы наше приложение работало эффективно. Веб-воркеры работают параллельно, именно поэтому перед нами встаёт, его величие, многопоточность. Но как это возможно, что в полностью однопоточном языке мы сталкиваемся с обратным утверждением? Всё потому, что JS – прежде всего язык, который не определяет модель потоков. Веб-воркеры не являются частью языка, это только возможность браузера, к которой мы можем получить доступ с помощью JS. Само собой, воркеры имеют обширный инструментарий, который позволяет использовать его, как отдельную вычислительную машину, что соответственно позволяет использовать новые способы оптимизации веб-приложения.

Конечно у web-workers есть тёмная сторона. Инструмент, который позволяет нам обойти минусы JS, конечно навязывает нам свои ограничения. Например, используя web-workers мы не можем взаимодействовать с DOM, различными объектами JS, которые позволяют нам “переписывать” документ.

В итоге мы приходим в тому, что браузер даёт нам возможность использовать Web-workers для обхода ограничений JavaScript, однако сам навязывает свои правила. Нужно понять, что Web-workers – это сравнительно новая технология, она не даёт что-то невероятно универсальное, но позволяет увидеть новые возможности в оптимизации, путём переноса выполнения ресурсоёмких операций в отдельные потоки.

Источники:

С подпиской рекламы не будет

Подключите Дзен Про за 159 ₽ в месяц