Реализация Web Workers для фоновых вычислений на сайте

Когда браузер зависает при обработке тяжёлых данных, пользователь уходит к конкурентам. Мы реализуем Web Workers для фоновых вычислений, чтобы интерфейс оставался отзывчивым даже при больших нагрузках. Наша команда берёт на себя весь цикл — от аудита до внедрения и поддержки.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Реализация Web Workers для фоновых вычислений на сайте
Средний
~2-3 дня
Часто задаваемые вопросы

Реализация Web Workers для фоновых вычислений на сайте

Представьте: пользователь загружает CSV-файл на 10 МБ, и интерфейс зависает на 5 секунд. Скролл не реагирует, кнопки не нажимаются. Это происходит, потому что JavaScript — однопоточный, и парсинг блокирует UI. Решение — выгрузка тяжёлых вычислений в Web Workers. Наш опыт: 10+ лет в высоконагруженных веб-приложениях, 50+ проектов с Workers, ускорение взаимодействия в 3–5 раз. Обращайтесь — мы внедрим многопоточность под ваш стек и сроки.

Web Workers не имеют доступа к DOM, window, document. Общение с main thread — только через сообщения (postMessage/onmessage). Это ограничение — защита от race conditions. Мы используем типизированные обёртки и Transferable Objects для эффективной передачи больших данных. Подробнее об API — в документации MDN.

Какие задачи решают Web Workers?

  • Парсинг и трансформация больших CSV/JSON (>1 MB) — ускорение в 4 раза за счёт выгрузки из основного потока.
  • Шифрование/дешифрование данных (AES, RSA) — занимает 500 ms в main thread, в Worker — без влияния на UI.
  • Рендеринг Canvas-графики через OffscreenCanvas — все рисование в фоне, основной поток только реагирует на ввод пользователя.
  • Алгоритмы поиска и сортировки на больших массивах (10⁶+ элементов) — с пулом Worker-ов параллельно за секунды.
  • Компрессия данных (pako, zlib) — сжатие перед отправкой на сервер без блокировки интерфейса.
  • Физические симуляции и ray tracing — для интерактивных визуализаций.

Как внедрить Web Workers без потери производительности?

Ключевые принципы:

  • Transferable Objects — передача ArrayBuffer, ImageBitmap, OffscreenCanvas по ссылке (без копирования) для больших данных.
  • Пул Worker-ов — по числу ядер CPU, распределение задач через единую очередь.
  • Типизированные обёртки — исключение ошибок типов при обмене сообщениями.
  • Обработка ошибок на уровне планировщика — перезапуск упавшего Worker-а без потери данных.
Метод передачи данных Скорость Поддержка Применение
Structured clone 1x (копирование) Все браузеры Мелкие данные (<1MB)
Transferable Objects 10x+ (без копирования) Chrome, Firefox, Safari 16.4+ ArrayBuffer, ImageBitmap
Подробнее о Transferable Objects Передача объектов по ссылке возможна для ArrayBuffer, ImageBitmap, OffscreenCanvas, ReadableStream, WritableStream. После вызова postMessage с массивом transferables исходный объект становится недоступен в отправителе, что исключает гонки данных. Это критично для буферов от 10 MB — задержка снижается в десятки раз.

Базовая структура Worker

// worker.ts — фоновый поток
self.onmessage = ({ data }: MessageEvent) => {
  const result = heavyComputation(data.payload);
  self.postMessage({ type: 'RESULT', payload: result });
};

// main.ts — основной поток
const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
worker.postMessage({ type: 'PROCESS', payload: largeArray });
worker.onmessage = (event) => console.log('Результат:', event.data.payload);
worker.onerror = (error) => console.error('Worker error:', error.message);
worker.terminate();

Типизированная обёртка

Работать с сырым postMessage неудобно. Типизированная обёртка решает это:

type WorkerMessage<T extends Record<string, unknown>> = {
  [K in keyof T]: { type: K; payload: T[K] }
}[keyof T];

interface WorkerRequest {
  SORT: { array: number[]; direction: 'asc' | 'desc' };
  FILTER: { data: Record<string, unknown>[]; query: string };
  PARSE_CSV: { content: string };
}

interface WorkerResponse {
  SORT_DONE: number[];
  FILTER_DONE: Record<string, unknown>[];
  PARSE_CSV_DONE: Record<string, string>[];
  ERROR: { message: string };
}

class TypedWorker {
  private worker: Worker;
  private pending = new Map<string, { resolve: Function; reject: Function }>();
  private seq = 0;

  constructor(workerUrl: URL) {
    this.worker = new Worker(workerUrl, { type: 'module' });
    this.worker.onmessage = ({ data }) => {
      const { id, type, payload } = data;
      const handler = this.pending.get(id);
      if (!handler) return;
      this.pending.delete(id);
      if (type === 'ERROR') handler.reject(new Error(payload.message));
      else handler.resolve(payload);
    };
  }

  send<K extends keyof WorkerRequest>(type: K, payload: WorkerRequest[K]): Promise<WorkerResponse[`${K}_DONE` & keyof WorkerResponse]> {
    return new Promise((resolve, reject) => {
      const id = String(++this.seq);
      this.pending.set(id, { resolve, reject });
      this.worker.postMessage({ id, type, payload });
    });
  }

  terminate() {
    this.worker.terminate();
  }
}

Передача больших данных: Transferable Objects

postMessage копирует данные по умолчанию. Для больших ArrayBuffer это дорого. Transferable Objects передаются по ссылке (owner transfer), без копирования:

const buffer = new ArrayBuffer(1024 * 1024 * 10); // 10 MB
const view = new Float32Array(buffer);
// Заполняем данными...
// Передаём без копирования — после этого buffer в main thread недоступен
worker.postMessage({ type: 'PROCESS', payload: buffer }, [buffer]);
// В Worker
self.onmessage = (event: MessageEvent) => {
  const buf = event.data.payload as ArrayBuffer;
  const view = new Float32Array(buf);
  // обрабатываем...
  ctx.postMessage({ type: 'DONE', payload: buf }, [buf]);
};

Transferable: ArrayBuffer, MessagePort, ImageBitmap, OffscreenCanvas, ReadableStream, WritableStream.

OffscreenCanvas — рендеринг в Worker

// main.ts
const canvas = document.getElementById('chart') as HTMLCanvasElement;
const offscreen = canvas.transferControlToOffscreen();
worker.postMessage({ type: 'INIT_CANVAS', canvas: offscreen }, [offscreen]);
worker.postMessage({ type: 'RENDER', data: chartData });

// chart-worker.ts
let ctx: OffscreenCanvasRenderingContext2D;
self.onmessage = (event: MessageEvent) => {
  const { type, canvas, data } = event.data;
  if (type === 'INIT_CANVAS') {
    ctx = canvas.getContext('2d')!;
    return;
  }
  if (type === 'RENDER') renderChart(ctx, data);
};

Пул Worker-ов для параллельной обработки

class WorkerPool {
    private workers: Worker[] = [];
    private queue: Array<{ resolve: Function; reject: Function; message: unknown }> = [];
    private idle: Worker[] = [];

    constructor(workerUrl: URL, poolSize = navigator.hardwareConcurrency || 4) {
        for (let i = 0; i < poolSize; i++) {
            const worker = new Worker(workerUrl, { type: 'module' });
            worker.onmessage = (event) => this.onWorkerMessage(worker, event);
            worker.onerror = (error) => this.onWorkerError(worker, error);
            this.workers.push(worker);
            this.idle.push(worker);
        }
    }

    execute(message: unknown): Promise<unknown> {
        return new Promise((resolve, reject) => {
            const task = { resolve, reject, message };
            const worker = this.idle.pop();
            if (worker) this.dispatch(worker, task);
            else this.queue.push(task);
        });
    }

    private dispatch(worker: Worker, task: { resolve: Function; reject: Function; message: unknown }) {
        (worker as any).__resolve = task.resolve;
        (worker as any).__reject = task.reject;
        worker.postMessage(task.message);
    }

    private onWorkerMessage(worker: Worker, event: MessageEvent) {
        (worker as any).__resolve?.(event.data);
        this.scheduleNext(worker);
    }

    private onWorkerError(worker: Worker, error: ErrorEvent) {
        (worker as any).__reject?.(new Error(error.message));
        this.scheduleNext(worker);
    }

    private scheduleNext(worker: Worker) {
        const next = this.queue.shift();
        if (next) this.dispatch(worker, next);
        else this.idle.push(worker);
    }

    terminate() {
        this.workers.forEach((w) => w.terminate());
    }
}

Как обеспечить отказоустойчивость при многопоточности?

Падение Worker-а не должно влиять на основное приложение. Для этого планировщик перезапускает упавший Worker и перераспределяет задачи. В пуле используем паттерн heartbeat: каждый Worker отправляет сигнал раз в 10 секунд. Если сигнал отсутствует 20 секунд — Worker считается упавшим и заменяется новым. Это даёт uptime 99,9% при штатных нагрузках.

Параметр Одиночный Worker Пул Worker-ов
Время обработки 100 задач по 100ms ~10 сек (последовательно) ~1.2 сек (8 Workers)
Отказоустойчивость Низкая (падение = потеря) Высокая (перезапуск)
Использование памяти Минимальное (1 поток) Умеренное (по числу потоков)

Что входит в работу

  • Анализ узких мест в текущем приложении (профилирование, Core Web Vitals).
  • Проектирование архитектуры: выбор количества Worker-ов, схемы передачи данных, стратегии fallback для старых браузеров.
  • Реализация типизированных обёрток и пула (или интеграция с React/Vue через кастомные хуки).
  • Покрытие unit-тестами (мокирование Worker-ов изолированно).
  • Документация и обучение команды.
  • Гарантия поддержки и доработок в течение месяца после сдачи.

Этапы работы

  1. Аудит — профилирование текущей производительности, выявление кандидатов для выноса.
  2. Проектирование — выбор паттерна (один Worker, пул, OffscreenCanvas), определение контрактов сообщений.
  3. Реализация — написание Worker-ов, интеграция, типизация.
  4. Тестирование — юнит-тесты, нагрузочное тестирование (имитация 20+ одновременных задач).
  5. Деплой и мониторинг — внедрение, отслеживание LCP/INP после релиза.

Почему выбирают нас?

Мы — команда инженеров с 10+ лет опыта в веб-разработке. Реализовали 50+ проектов с Web Workers — от парсинга логов на клиенте до real-time дашбордов с OffscreenCanvas. Наши решения ускоряют вычисления в 3–5 раз без потери стабильности. Свяжитесь с нами — мы оценим ваш проект и предложим сроки от 1 до 5 дней в зависимости от сложности. Закажите внедрение Web Workers прямо сейчас.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060