Используем веб-воркеры для улучшения производительности

О сервис-воркерах слышали все. А вот когда разговор заходит о веб-воркерах, часто я встречаю вопрос «А что это?». В этом посте расскажу подробнее, что это такое и когда эту технологию стоит использовать.

Что за зверь

Веб-воркер — штука, которая умеет выполняться в браузере параллельно с основным скриптом. А значит — выполняться, не блокируя отрисовку страницы. Проще говоря, если запустить что-то тяжёлое в основном скрипте, то страница будет тормозить, а если в веб-воркере — то не будет.

Что нужно знать перед началом

  • Джаваскрипт всё-таки однопоточный, поэтому если мы хотим запустить какую-то задачу параллельно, то она должна лежать в отдельном файле.
  • Веб-воркер не имеет доступа ни к DOM-дереву, ни к объекту window. Напрямую к локальному хранилищу обратиться внутри него тоже нельзя. Всё потому, что работа с DOM-деревом и локальным хранилищем — последовательная, а веб-воркер работает параллельно.
  • Общаться с веб-воркером придётся через сообщения, которые нужно пересылать между ним и основным скриптом. Но стоит учитывать, что передача объектов по ссылке внутрь воркера не сработает, объекты будут скопированы перед отправкой.

API

У веб-воркеров простое API. Чтобы проверить, поддерживается ли технология браузером:

if (window.Worker) {...}

Чтобы создать воркер:

const worker = new Worker('./path/to/file.js');

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

Чтобы отправить сообщение из основного скрипта в воркер:

worker.postMessage({ key: 'value' });

Чтобы подписаться на сообщение внутри воркера:

self.onmessage = (e) => console.log(e);

// `self` — глобальный объект внутри воркера,
// как `window` в основном скрипте

Чтобы отправить сообщение из воркера в основной скрипт:

self.postMessage({ key: 'value' });

И чтобы подписаться на сообщение от воркера в основном скрипте:

worker.onmessage = (e) => console.log(e);

Пример

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

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

Без применения веб-воркера анимация стопорится, и FPS падает до 0. Так происходит потому, что процессор занят сложной задачей. На скриншоте видно, сколько места занял response — это вычисления:

Иллюстрация распределения процессорного времени
Иллюстрация распределения процессорного времени

Веб-воркер выполняется параллельно, поэтому анимация в среднем держится на уровне 60 FPS. На скриншоте видно, что вычисления теперь выполняются отдельно:

Иллюстрация влияния веб-воркера на FPS
Иллюстрация влияния веб-воркера на FPS

Результат для анимации без CSS-трансформаций видно и невооружённым глазом. Попробуйте покликать на разные кнопки сами.

CSS-трансформации спасают положение, страница становится отзывчивее, а анимация глаже. Но даже с ними при чуть больших нагрузках FPS падает до 45–50. С веб-воркером FPS стабильно держится на 60.

Зачем это

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

Кто уже использует

Покедекс использует веб-воркер для обзора изменений в shadow DOM. Из-за этого приложение плавно скроллится и работает без подмерзаний даже на старых телефонах.

И на сладкое

Веб-воркеры поддерживаются всеми браузерами, кроме Оперы Мини. Они работают даже в 11-м IE:

Поддержка браузерами
Поддержка браузерами

Поэтому пользуйтесь, классная же технология 🙃

Доп. материалы

Немного теории