Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Многопоточность в JavaScript: Web Workers
JavaScript исторически был однопоточным, но современный стандарт предоставляет способ достичь параллелизма через Web Workers. Это механизм, который позволяет выполнять код в отдельных потоках без блокирования главного потока UI.
Проблема: однопоточность JavaScript
JavaScript работает в одном потоке — Event Loop. Если выполняется дорогая операция, UI зависает:
// Плохо: блокирует UI
function expensiveCalculation() {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += i;
}
return result;
}
button.addEventListener('click', () => {
const result = expensiveCalculation(); // UI зависает на несколько секунд!
console.log(result);
});
Решение 1: Web Workers для параллельного выполнения
Web Workers позволяют выполнять код в отдельном потоке:
worker.js (отдельный файл)
// Этот код выполняется в отдельном потоке
self.addEventListener('message', (event) => {
const { data } = event;
if (data.type === 'calculate') {
// Дорогая операция в отдельном потоке
let result = 0;
for (let i = 0; i < data.count; i++) {
result += i;
}
// Отправить результат обратно в главный поток
self.postMessage({ result });
}
});
main.js (главный поток)
// Создать worker
const worker = new Worker('worker.js');
button.addEventListener('click', () => {
// Отправить задачу в worker
worker.postMessage({ type: 'calculate', count: 1000000000 });
console.log('Расчет начался, UI не зависает!');
});
// Слушать результат из worker
worker.addEventListener('message', (event) => {
const { result } = event.data;
console.log('Результат:', result);
});
Важные свойства Web Workers
1. Web Workers выполняются в отдельном потоке
// worker.js
console.log('Я в worker!');
// Нет доступа к:
// - DOM элементам
// - window объекту (есть self вместо этого)
// - localStorage (не рекомендуется)
// - родительскому документу
// Есть доступ к:
// - navigator, location
// - XMLHttpRequest, fetch
// - setTimeout, setInterval
// - обработчикам сообщений (postMessage)
2. Обмен данными через postMessage
// main.js -> worker.js
const worker = new Worker('worker.js');
// Отправить сообщение
worker.postMessage({
type: 'process',
data: [1, 2, 3, 4, 5],
timestamp: Date.now()
});
// worker.js получит сообщение
3. Worker может отправить сообщение обратно
// worker.js
self.addEventListener('message', (event) => {
const { data } = event;
const result = data.reduce((a, b) => a + b);
// Отправить результат
self.postMessage({ success: true, result });
});
Практический пример: обработка изображения
// image-worker.js
self.addEventListener('message', (event) => {
const { imageData } = event.data;
// Обработка изображения в отдельном потоке
const pixels = imageData.data;
for (let i = 0; i < pixels.length; i += 4) {
// Преобразование в оттенки серого
const red = pixels[i];
const green = pixels[i + 1];
const blue = pixels[i + 2];
const gray = (red + green + blue) / 3;
pixels[i] = gray;
pixels[i + 1] = gray;
pixels[i + 2] = gray;
}
// Отправить обработанное изображение
self.postMessage({ imageData });
});
// main.js
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const worker = new Worker('image-worker.js');
worker.postMessage({ imageData });
worker.addEventListener('message', (event) => {
const { imageData } = event.data;
ctx.putImageData(imageData, 0, 0);
console.log('Изображение обработано!');
});
Способ 2: Inline Workers (не всегда работают)
// Создать worker из функции (экспериментальное)
const code = `
self.addEventListener('message', (event) => {
const result = event.data * 2;
self.postMessage(result);
});
`;
const blob = new Blob([code], { type: 'application/javascript' });
const workerUrl = URL.createObjectURL(blob);
const worker = new Worker(workerUrl);
worker.postMessage(5);
worker.addEventListener('message', (e) => {
console.log(e.data); // 10
});
Способ 3: Shared Workers (несколько вкладок)
// shared-worker.js
const connections = [];
self.addEventListener('connect', (event) => {
const port = event.ports[0];
connections.push(port);
port.addEventListener('message', (event) => {
// Отправить сообщение всем подключенным вкладкам
connections.forEach(p => {
p.postMessage(event.data);
});
});
port.start();
});
// main.js
const worker = new SharedWorker('shared-worker.js');
worker.port.addEventListener('message', (event) => {
console.log('От другой вкладки:', event.data);
});
worker.port.postMessage({ message: 'Привет из этой вкладки!' });
worker.port.start();
Ограничения Web Workers
// Web Workers НЕ имеют доступа к:
// - DOM (нельзя обновлять UI прямо)
// - window объекту
// - document объекту
// - parent объекту
// Решение: отправлять результаты в главный поток
// и обновлять UI оттуда
Реальный пример: обработка больших данных
// data-processor.js
self.addEventListener('message', (event) => {
const { data, operation } = event.data;
if (operation === 'filter') {
const filtered = data.filter(item => item.value > 100);
self.postMessage({ type: 'filter', result: filtered });
} else if (operation === 'map') {
const mapped = data.map(item => ({ ...item, doubled: item.value * 2 }));
self.postMessage({ type: 'map', result: mapped });
} else if (operation === 'sort') {
const sorted = data.sort((a, b) => a.value - b.value);
self.postMessage({ type: 'sort', result: sorted });
}
});
// main.js
const worker = new Worker('data-processor.js');
const largeDataset = Array.from({ length: 1000000 }, (_, i) => ({
id: i,
value: Math.random() * 1000
}));
// Обработка без блокировки UI
worker.postMessage({ data: largeDataset, operation: 'filter' });
worker.addEventListener('message', (event) => {
const { type, result } = event.data;
console.log(`${type} завершена. Результатов: ${result.length}`);
displayResults(result);
});
Когда использовать Web Workers
- Сложные вычисления (факториал, фибоначчи, криптография)
- Обработка больших данных (сортировка, фильтрация массивов)
- Обработка изображений/видео (фильтры, преобразования)
- Синтаксический анализ (парсинг JSON, XML)
- Криптографические операции
Когда НЕ использовать:
- Если операция быстрая (<1ms)
- Если нужен прямой доступ к DOM
- Если много мала данных для обмена
Шпаргалка: Web Workers vs главный поток
| Параметр | Главный поток | Web Worker |
|---|---|---|
| Доступ к DOM | Да | Нет |
| Доступ к window | Да | Нет (только self) |
| Скорость запуска | Сразу | Задержка (создание потока) |
| Общая память | Да | Нет (копируется данные) |
| Идеален для | UI логика | Дорогие вычисления |
JavaScript не многопоточный в классическом смысле, но Web Workers дают близкий к этому функционал для параллельного выполнения кода и обхода однопоточной архитектуры Event Loop.