Как использовать Web Workers в Angular?angular-70

Web Workers позволяют выносить ресурсоемкие вычисления в отдельные потоки, предотвращая блокировку основного потока браузера. В Angular есть несколько способов их интеграции.


1. Создание Web Worker через Angular CLI

Самый простой способ - генерация через CLI:

ng generate web-worker app

Эта команда:

  1. Создает файл src/app/app.worker.ts
  2. Модифицирует tsconfig.json для поддержки workers
  3. Добавляет пример использования в основной код

2. Базовая архитектура взаимодействия

Worker :

/// <reference lib="webworker" />

addEventListener('message', ({ data }) => {
  const result = heavyCalculation(data);
  postMessage(result);
});

function heavyCalculation(data: any): any {
  // Ресурсоемкие вычисления
  return data.map(/* ... */);
}

Основной поток :

const worker = new Worker(new URL('./app.worker', import.meta.url), {
  type: 'module' // Важно для Angular
});

worker.postMessage(largeDataSet);

worker.onmessage = ({ data }) => {
  console.log('Получены данные от worker:', data);
};

3. Оптимизация взаимодействия

a) Обертка в Observable :

import { Observable, Observer } from 'rxjs';

function runInWorker<T>(worker: Worker, data: any): Observable<T> {
  return new Observable((observer: Observer<T>) => {
    worker.postMessage(data);

    worker.onmessage = ({ data }) => {
      observer.next(data);
      observer.complete();
    };

    worker.onerror = (error) => {
      observer.error(error);
    };
  });
}

b) Управление жизненным циклом:

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

4. Использование с Angular Services

Лучшая практика - вынести логику работы с worker в сервис:

@Injectable({ providedIn: 'root' })
export class WorkerService {
  private worker: Worker;

  constructor() {
    this.worker = new Worker(new URL('./app.worker', import.meta.url), {
      type: 'module'
    });
  }

  processData(data: any): Observable<any> {
    return new Observable(observer => {
      this.worker.onmessage = ({ data }) => {
        observer.next(data);
        observer.complete();
      };

      this.worker.postMessage(data);
    });
  }
}

5. Ограничения и особенности

  1. Нет доступа к DOM:

    • Workers работают в изолированном контексте
    • Не могут использовать document, window
  2. Передача данных:

    • Данные передаются через structured cloning
    • Для больших объектов используйте Transferable Objects
// Пример с Transferable
const arrayBuffer = new ArrayBuffer(1024);
worker.postMessage(arrayBuffer, [arrayBuffer]);
  1. Поддержка браузеров:
    • Все современные браузеры поддерживают workers
    • Для legacy может потребоваться полифил

6. Альтернативы для сложных сценариев

a) Comlink - упрощение API:

import * as Comlink from 'comlink';

// В worker
const api = { heavyTask: () => { /* ... */ } };
Comlink.expose(api);

// В основном потоке
const worker = new Worker('./worker', { type: 'module' });
const api = Comlink.wrap(worker);
await api.heavyTask();

b) Worker-loader :

import Worker from 'worker-loader!./app.worker';

const worker = new Worker();

Резюмируем

  1. Генерация: Используйте ng generate web-worker
  2. Архитектура: Основной поток ↔ Worker через postMessage/onmessage
  3. Интеграция: Оберните в сервис для удобного использования
  4. Оптимизация: Transferable Objects для больших данных
  5. Ограничения: Нет доступа к DOM, изолированное выполнение

Идеальные кейсы для Web Workers:

  • Обработка больших массивов данных
  • Сложные математические вычисления
  • Анализ/парсинг файлов
  • Фоновые процессы

Для большинства Angular-приложений достаточно встроенной поддержки workers через CLI. Для сложных сценариев рассмотрите Comlink или worker-loader.