Что такое…

Что такое Web Worker — и почему await не спасает страницу от зависания

Иллюстрация: официант спокойно разносит заказы, а на кухне за стеклом второй повар месит огромное тесто

Смотри, знакомая картина. Ты нажимаешь «Обработать» на странице с большим файлом. Спиннер замирает. Кнопка «Отмена» не нажимается. Даже текст не выделяется. Через пять секунд всё оживает разом.

Самое обидное: функция была async, и ты честно поставил await. Не помогло. Сейчас разберёмся почему — и что помогает на самом деле.

Почему страница замирает

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

Представь кафе с одним официантом. Он принимает заказы, разносит блюда и считает выручку. Пока он пять минут пересчитывает кассу, к нему не подойти. Зал «завис».

А что делает await? Он хорош, когда ты ждёшь — ответа сервера, таймера, файла. Пока идёт ожидание, поток свободен. Подробно это в статье про асинхронность.

Но цикл на миллион строк — не ожидание, а работа. Её всё равно кто-то должен сделать. И делает её тот же единственный официант. async перед функцией не нанимает второго.

Web Worker — это второй поток

Web Worker — это отдельный файл с JavaScript, который браузер запускает в своём потоке, рядом с главным. Это второй повар на кухне. Он месит тесто сколько угодно, а официант тем временем обслуживает зал.

Создаётся он одной строкой. Вот как это выглядит в проекте на Vite:

// main.js — страница
const worker = new Worker(new URL("./worker.js", import.meta.url), { type: "module" });

worker.postMessage(bigList);          // отдали работу
worker.onmessage = (e) => {
  console.log("готово:", e.data);    // получили результат
};
// worker.js — второй поток
self.onmessage = (e) => {
  const result = heavySort(e.data);   // хоть 5 секунд — страница живая
  self.postMessage(result);
};

Всё общение — через сообщения: postMessage туда, onmessage обратно. Как записки через окно раздачи.

Три вещи, которые удивляют всех

1. У воркера нет страницы. В нём нет document и window. Он не может сам поменять кнопку или текст — DOM живёт только в главном потоке. Воркер считает и отдаёт результат, а рисует уже страница.

2. Данные копируются, а не передаются. Когда ты шлёшь массив в воркер, браузер делает его полную копию. Поменяешь копию в воркере — на странице ничего не изменится. А огромный массив копировать долго, иногда дольше самой работы.

3. Копию можно заменить передачей. Для двоичных данных (ArrayBuffer) есть трюк: отдать их «из рук в руки».

worker.postMessage(buffer, [buffer]);
console.log(buffer.byteLength); // 0 — у страницы буфера больше нет

Копирования нет, передача мгновенная. Но у отправителя остаётся пустой буфер на 0 байт. Если дальше в коде страница читает его — получишь нули и долго будешь искать баг.

4. Воркер сам не уходит. Поток живёт, пока его не остановят. Частая ошибка — создавать new Worker(...) на каждый клик. Через сотню кликов у тебя сотня потоков, и вкладка ест память. Создай воркер один раз и переиспользуй его. А когда он больше не нужен — worker.terminate().

Когда воркер нужен, а когда нет

Воркер — не бесплатный. Запуск отдельного потока и пересылка данных тоже стоят времени. Правило на глаз:

  • Нужен, если операция заметно тормозит интерфейс: парсинг большого CSV, обработка фото, сжатие, поиск по десяткам тысяч записей.
  • Нужен для ИИ прямо в браузере. Библиотеки вроде Transformers.js советуют запускать модель в воркере — иначе страница встанет на время ответа.
  • Не нужен для запросов к серверу. Это ожидание, с ним справляется await.
  • Не нужен, если задача идёт 20 миллисекунд. Часто её дешевле ускорить: например, убрать вложенный цикл. Как это найти — в статье про сложность алгоритмов.

Как проверить, что проблема в главном потоке: открой DevTools → Performance, запиши клик. Длинная серая полоса с красным уголком — это Long Task. Всё, что дольше 50 мс, пользователь чувствует как подтормаживание.

Web Worker и Service Worker — это одно и то же?

Нет. Web Worker нужен твоей странице для тяжёлых вычислений и живёт, пока открыта вкладка. Service Worker стоит между сайтом и сетью: кэширует файлы и работает офлайн.

Можно ли из воркера сделать fetch-запрос?

Да. В воркере есть fetch, таймеры, IndexedDB и WebSocket. Нет только того, что связано со страницей: DOM, window, localStorage.

Как попросить ИИ вынести код в воркер?

Скажи конкретно: «вынеси функцию X в Web Worker, страница шлёт данные через postMessage, ArrayBuffer передавай через transfer». Без этого ИИ часто просто добавит async — а это, как мы выяснили, не поможет.

Учись вайб-кодингу, а не просто читай о нём

Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.

Открыть приложение
Робот KODiQ

ИИ-редактор KODiQ. Пишет про вайб-кодинг и AI-инструменты простым языком — каждый день.

Все статьи →