Связь между циклом событий и механизмом JS и механизмом рендеринга

внешний интерфейс браузер
Связь между циклом событий и механизмом JS и механизмом рендеринга

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

Надеюсь ответить на ваши следующие вопросы:

  • Что такое конвейер компиляции движка JS
  • Что делает процесс рендеринга
  • Зачем нужен цикл событий
  • В чем разница между различными средами хостинга JS
  • Какие проблемы решили микрозадачи и проверки
  • Является ли requestAnimationFrame макрозадачей или микрозадачей
  • Когда выполняется requestIdleCallback

JS-движок

сочинение

Движок js включает в себя синтаксический анализатор, интерпретатор, gc и JIT-компилятор.

  • парсер: отвечает за преобразование исходного кода javascript в AST
  • Интерпретатор: Интерпретатор, отвечающий за преобразование AST в байт-код, а также за интерпретацию и выполнение.
  • JIT-компилятор: скомпилируйте функции горячей точки во время выполнения, преобразуйте байт-код в машинный код, а затем непосредственно выполните машинный код.
  • gc (сборщик мусора): сборщик мусора, очищает объекты в памяти кучи, которые больше не используются

конвейер компиляции

Конвейер компиляции общего механизма JS состоит в том, чтобы разобрать исходный код в AST, затем преобразовать AST в байт-код, интерпретировать и выполнить байт-код. Среда выполнения будет собирать частоту выполнения функции, для горячего кода, достигшего определенного порога, будет преобразовывать соответствующий байт-код в машинный код (JIT) и выполнять его напрямую. Это процесс, в котором код js может вступить в силу.

движок рендеринга

При рендеринге html и css будут разобраны на dom и cssom с помощью парсера соответственно, а затем объединены вместе, а стиль макета будет рассчитан в абсолютных координатах, будет сгенерировано дерево рендеринга, а затем содержимое дерева рендеринга будет копируется в видеопамять, а рендеринг может выполняться видеокартой.

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

Как совместить движок JS и движок рендеринга

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

Есть две идеи:

Многопоточность

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

Архитектура пользовательского интерфейса Android

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

электронная архитектура пользовательского интерфейса

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

image.png

Из приведенных выше двух случаев мы можем сделать вывод, чтоДизайн всех систем пользовательского интерфейса, если используется многопоточная (процессная) архитектура, в основном заключается в том, что пользовательский интерфейс может работать только в одном потоке (процессе), а другие потоки (процессы) отправляют сюда сообщения для обновления, если несколько потоков , будет очередь сообщений и лупер. Создателем очереди сообщений является каждый дочерний поток (процесс), а потребителем — основной поток (процесс).

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

серверная очередь сообщений

Поскольку грузоподъемность различных служб на серверной части различается, посередине будет добавлена ​​очередь сообщений для асинхронной обработки сообщений.В отличие от архитектуры пользовательского интерфейса внешнего клиента, промежуточное программное обеспечение внутренней очереди сообщений будет иметь несколько потребителей и несколько очередей, в то время как очередь сообщений системы пользовательского интерфейса имеет только одну очередь и одного потребителя (основной поток, основной процесс)..

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

один поток

Поскольку javascript изначально был разработан для обработки форм, особо большого объема вычислений не было бы, поэтому вместо использования многопоточной архитектуры операции dom и логические вычисления выполнялись в одном потоке, а рендеринг и выполнение JS блокировали друг друга. . (позже добавлен веб-воркер, но не основной)

Мы знаем, что движок JS знает только, как выполнять JS, а движок рендеринга знает только, как рендерить. Они не знают друг друга. Как сотрудничать?

Ответ — цикл событий.

хост-среда

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

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

Внедренный глобальный API отличается
  • Node внедрит некоторые глобальные требования API и предоставит встроенные модули, такие как fs, os и т. д.
  • Браузер внедрит стандартный API w3c
  • Кросс-энд движок введет API устройства, а также введет набор API для работы пользовательского интерфейса (может быть API бенчмарка w3c или нет)
Реализация цикла событий отличается

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

  • Браузер в основном планирует рендеринг и выполнение JS, а также рабочие процессы.
  • Node в основном планирует различные операции ввода-вывода
  • Кросс-энд движок также планирует рендеринг и выполнение JS.

Здесь нас интересует только цикл обработки событий в браузере.

цикл событий браузера

check

Выполнение задачи JS в браузере представляет собой цикл событий. В конце каждого цикла он проверяет, нужно ли его отрисовывать и нужно ли обрабатывать сообщение воркера. Благодаря этому методу проверки каждый раз, когда цикл заканчивается , интегрированы рендеринг, выполнение JS, worker и т. д., так что все они могут выполняться в одном потоке (рендеринг на самом деле находится в другом потоке, но он будет блокировать друг друга с потоком JS).

Это решает проблемы планирования рендеринга, выполнения JS и рабочих процессов.

Но есть ли с этим проблемы?

Мы продолжим ставить новые задачи в очередь задач, чтобы если есть более качественные задачи, они не выполнялись, пока не будут выполнены все задачи. А если "срочно"?

Так что этого недостаточно, чтобы добавить в цикл событий быстрый канал для «аварийной» обработки, то есть микрозадачи.

micro tasks

Задание по-прежнему выполняется по одному.После выполнения проверяется, рендерить или нет, и обрабатывать рабочие сообщения.Однако в высокоприоритетную "аварийку" также добавлен механизм вырезания очереди.После выполнения задания, все срочные дела (микрозадачи) все обрабатываются.

Таким образом, цикл обработки событий кажется идеальным, он будет каждый раз проверять, выполнять рендеринг или нет, а также может быстрее обрабатывать «аварийные ситуации» JS.

requestAnimationFrame

После выполнения JS будет жизненный цикл до начала рендеринга, то есть requestAnimationFrame, здесь наиболее уместно провести некоторые вычисления, и можно гарантировать, что вычисления должны быть выполнены до рендеринга.

image.png

Если кто-то спросит, является ли requestAnimationFrame макрозадачей или микрозадачей, вы можете сказать ему:requestAnimationFrame — это функция обратного вызова, которая выполняется каждый раз, когда цикл завершается и требует рендеринга, а не макро- или микрозадача.

Проблема с циклом событий

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

Расчет и рендеринг каждого кадра имеют фиксированную частоту.Если время выполнения JS слишком велико и превышает время обновления одного кадра, это вызовет задержки рендеринга и даже пропадение кадров (поскольку данные предыдущего кадра не отрендерились к интерфейсу). Он будет перезаписан новыми данными), давая пользователю ощущение «интерфейсной карты».

Что может привести к задержке обновления кадров или даже к перезаписи данных кадров (пропуску кадров)? Каждый цикл возможен на каждом этапе перед отрисовкой чека, то есть задача, микротаск, requestAnimationFrame, requestIdleCallback могут заблокировать проверку, поэтому при проверке окажется, что она отрендерилась, и повторно отрендерить будет уже поздно.

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

В дополнение к самому JS-коду, если браузер может предоставить API для выполнения в каждом интервале кадра, тогда он не будет блокироваться, поэтому позже будет requestIdeCallback.

requestIdleCallback

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

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

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

Суммировать

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

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

Обновление кадра нельзя откладывать, иначе он заморозит или даже сбросит кадр, поэтому необходимо не делать слишком много вычислений в коде JS, поэтому с API requestIdleCallback я надеюсь выполнить его, когда еще есть время после каждого проверять, а не выполнять, если нет времени (время крайнего срока также используется в качестве параметра, который js-код может судить сам по себе). Это.

Предотвращение рендеринга кадров из-за слишком долгого времени расчета — это проблема, которая всегда волновала ui framework, то есть как не блокировать рендеринг, чтобы логику можно было разбить на маленькие кусочки, которые можно выполнить внутри интервала кадра. Браузер предоставляет апи холостого обратного вызова, а многие UI-фреймворки также реализуют разбиение суммы расчета путем рекурсивного изменения цикла и последующей записи состояния.Цель только одна: выполнение логики в цикле не может блокировать проверку, т. е. , он не может блокировать механизм рендеринга от создания кадров. Так что, будь то макрос кода JS и микрозадачи, requestAnimationCallback, requestIdleCallback не может рассчитывать слишком долго. Эта проблема — постоянная боль в фронтенд-разработке.