Браузер и фронтенд-рантайм
Что такое воркеры и зачем они нужны
Запарсите JSON-файл на 5 МБ на главном потоке — и страница замирает на ~150 мс: пропущенные кадры, неотзывчивые кнопки. Ответ браузера — не «ускорить поток». Ответ — «дать вам больше потоков».
Проблема главного потока
У браузера один поток на JavaScript, компоновку, отрисовку и ввод. Пока он занят, ничего больше не работает. Парсить большой файл, считать хэш или фильтровать огромный датасет на главном потоке — значит тормозить любую анимацию и любой отклик на всё это время.
Ответ браузера — выполнение вне главного потока: воркеры. Существует четыре вида, каждый для своей работы:
- Web worker — второй JS-поток для CPU-bound работы (парсинг, крипто, обработка изображений). Нет DOM, нет
window, нетdocument. - Service worker — сетевой прокси, сидящий между страницей и сервером. Перехватывает запросы, отдаёт закешированные ответы, переживает закрытие страницы.
- Worklet — крошечный хук в пайплайн рендеринга или аудио браузера (paint worklet, audio worklet). Ещё более ограничен, чем воркер.
- SharedArrayBuffer — общая память между потоками (не воркер сам по себе, но побег из message-passing). За гейтом заголовков cross-origin isolation.
- Старт web worker
- 5–20 мс
- Парсинг 5 МБ JSON, главный поток
- ~150 мс заморозки
- Парсинг 5 МБ JSON, web worker
- 0 мс джанка
- Бюджет кадра при 60 fps
- 16.67 мс
- Кадров пропущено при 150 мс
- ~9 кадров
Метафора офиса
Главный поток — это вы за своим столом: вы отвечаете на телефон (ввод) и обновляете доску (DOM) сами. Web worker — коллега в задней комнате: вы отправляете ему большой расчёт запиской, он делает его, не трогая ваш стол, присылает ответ запиской. Трогать вашу доску он не может. Только вы можете.
Service worker — почтовая комната: каждое письмо в и из проходит сначала через неё, и она может ответить на некоторые из своего шкафа, не выходя наружу. Продолжает работать, даже когда вы ушли на день.
Антон загружает фото для редактирования. Дима комментирует: «Применить фильтр — это миллионы пиксельных операций — на главном потоке это 600 мс заморозки. Поэтому мы постим данные изображения в web worker. Воркер перемалывает пиксели на своём потоке; главный поток остаётся свободным. Когда он заканчивает, постит результат обратно, и мы рисуем.»
Фундаментальное правило
Воркеры не имеют никакого доступа к DOM. Глобальный объект воркера — WorkerGlobalScope, в котором нет document, нет window, нет элементов. Это не ограничение, которое надо обходить — это дизайн. Он сохраняет DOM как single-owner и исключает состояния гонки. Воркер может вычислить что отрендерить и вернуть результат, но главный поток должен применить его.
Единственное исключение: OffscreenCanvas. Bitmap-канвас не является частью DOM-дерева — это просто буфер пикселей. Вы можете передать OffscreenCanvas воркеру, и он рисует в него целиком вне главного потока. Всё остальное в DOM — только главный поток.
Страница отдаёт тяжёлый расчёт web worker-у. Расставьте шаги в порядке, в котором они происходят.
- 1 Главный поток создаёт воркер: new Worker('calc.js')
- 2 Главный поток шлёт входные данные: worker.postMessage(data)
- 3 Воркер гоняет расчёт на своём потоке
- 4 Воркер шлёт результат обратно: postMessage(result)
- 5 Обработчик onmessage главного потока получает результат
Почему web worker не может обновить страницу напрямую?
Чем service worker отличается от web worker?
В метафоре офиса коллега в задней комнате делает тяжёлые расчёты для вас и отчитывается запиской, но не может тронуть вашу доску. Какой это вид воркера?
Страница парсит JSON-файл на 5 МБ на главном потоке, и это занимает примерно 150 мс. Бюджет кадра при 60 fps — 16.67 мс. Примерно сколько кадров пропускает один этот парсинг?
Почему это работает
Воркеры появились потому, что JavaScript однопоточен по дизайну — совместное использование изменяемого состояния несколькими потоками требует блокировок, а блокировки в UI-контексте вызывают дедлоки и priority inversion. Модель воркеров избегает всего этого, полностью запрещая общее состояние. У каждого воркера собственный heap, собственные глобалы, собственный event loop. Данные пересекают границу только через postMessage (который копирует их) или через явные примитивы общей памяти (SharedArrayBuffer + Atomics). Ограничение — это гарантия безопасности.
- 01В чём фундаментальная разница между web worker и service worker?
- 02Почему у воркеров нет доступа к DOM?
- 03Каково единственное DOM-исключение для воркеров?
Главный поток браузера — единственный владелец JS-выполнения, компоновки, отрисовки и ввода: всё, что его блокирует, тормозит всю страницу. Воркеры дают дополнительные потоки: web workers для CPU-bound вычислений (нет DOM, общение через postMessage), service workers для сетевого проксирования (перехват fetch, отдача кеша, переживают закрытие страницы). Ни один не может трогать DOM — это ограничение и есть то, что делает их безопасными. Парсинг JSON 150 мс на главном потоке пропускает ~9 кадров при 60 fps; перенесённый в web worker, он ничего не стоит главному потоку.
встречается в41
- Federation и lookahead: батчинг за пределами DataLoadermiddle
- Senior GraphQL API: scheduling-контракт, изоляция арендаторов, наблюдаемостьsenior
- Лок и single-flight: ограничение параллельных rebuildmiddle
- Stale-while-revalidate и CDN request coalescingmiddle
- Детектирование stampede и дизайн TTL для продакшенаmiddle
- Метастабильный сбой, fencing-токены и production-постмортемыsenior
- Что такое отношение: таблицы, строки, ключи и ограниченияjunior
- Ограничения, ключи и типы данных Postgresmiddle
- JSONB, массивы и когда side table побеждаетmiddle
- Целостность схемы: deferral, версионирование и сбои в продакшнеsenior
- Где происходит data fetching — и почему это решает LCPjunior
- React Server Components и Suspense streamingmiddle
- Senior internals: RSC payload, слои кэша и production паденияsenior
- Конверт IPjunior
- Читаем IP-заголовокmiddle
- Что делает TLS и зачем он нуженjunior
- Расписание ключей, SNI, ALPN и расширенияsenior
- Защита 0-RTT, ECH, гибридный PQ и продакшн TLSsenior
- Двенадцать слоёв: один URL, семь действующих лицjunior
- Устойчивость: каскадные повторы, circuit breakers и error budgetsenior
- Что такое OpenTelemetry: API, SDK, Collector, OTLPjunior
- Сигналы OTel, Semantic Conventions и проводной формат OTLPmiddle
- Collector OTel: receivers, processors, exporters и паттерны развёртыванияmiddle
- Vendor-нейтральность, eBPF-инструментирование, Operator и OTel в браузере и serverlesssenior
- Эксплуатация OTel Collector: надёжность, version skew, режимы отказа и управлениеsenior
- Что такое trace propagation и почему сломанная propagation хуже отсутствия трейсовjunior
- traceparent и tracestate: полный формат W3C-заголовкаmiddle
- Baggage и async-границы: перенос контекста через очереди и callback''''иmiddle
- Async context на разных языках, service mesh, миграция B3 и безопасностьsenior
- Production-сбои propagation, span links и платформенный дизайнsenior
- Debugging-воронка: SLO → RED → trace → profilejunior
- Архитектура OTel: один SDK, четыре сигнала, один wire-форматmiddle
- Петля инцидента: от пейджера до постмортема до предотвращенияmiddle
- Масштаб, безопасность и ROI наблюдаемых системsenior
- At-most-once, at-least-once, exactly-once: три контракта доставкиjunior
- Consumer-side dedup: самый дешёвый путь к exactly-once processingmiddle
- Exactly-once в production: impossibility-доказательство, гибридные паттерны и реальные инцидентыsenior
- Что такое OAuth и почему пароли — не ответjunior
- Authorization code flow с PKCEmiddle
- Sender-constrained токены: DPoP и mTLSsenior
- OAuth в production: audience атаки, observability и реальные провалыsenior