Как организовать параллельное выполнение кода в браузере?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Параллельное выполнение кода в браузере
Браузер работает на базе single-threaded event loop, но существует несколько механизмов для организации параллельного выполнения: Web Workers, Promises, async/await, requestIdleCallback, requestAnimationFrame и микротаски. Каждый подход имеет свои применения.
1. Web Workers - истинный параллелизм
Web Workers позволяют запустить код в отдельном потоке без блокирования UI:
// main.js
const worker = new Worker('worker.js');
function heavyComputation() {
const largeArray = Array.from({ length: 1000000 }, (_, i) => i);
worker.postMessage({ array: largeArray });
}
worker.onmessage = (event) => {
console.log('Result:', event.data);
};
worker.onerror = (error) => {
console.error('Worker error:', error.message);
};
// worker.js
self.onmessage = (event) => {
const { array } = event.data;
const result = array
.map(x => Math.sqrt(x))
.filter(x => x > 100)
.reduce((a, b) => a + b, 0);
self.postMessage({ result });
};
2. Promise и async/await
Микротаски в очереди Promise позволяют распределить работу:
async function processDataInChunks(items, processor) {
const results = [];
for (let i = 0; i < items.length; i++) {
const result = processor(items[i]);
results.push(result);
if (i % 10 === 0) {
await Promise.resolve();
}
}
return results;
}
3. requestIdleCallback
Выполнение когда браузер свободен:
function scheduleIdleWork(tasks) {
let taskIndex = 0;
function executeTask(deadline) {
while (taskIndex < tasks.length && deadline.timeRemaining() > 1) {
tasks[taskIndex]();
taskIndex++;
}
if (taskIndex < tasks.length) {
requestIdleCallback(executeTask);
}
}
requestIdleCallback(executeTask);
}
4. requestAnimationFrame для анимаций
function animateProgress(element, targetValue, duration = 1000) {
let startTime = null;
function animate(currentTime) {
if (!startTime) startTime = currentTime;
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const value = Math.floor(progress * targetValue);
element.textContent = value;
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
}
5. setTimeout для распределения
function executeInBatches(items, batchSize = 100) {
let index = 0;
return new Promise((resolve) => {
function processBatch() {
const end = Math.min(index + batchSize, items.length);
for (; index < end; index++) {
processItem(items[index]);
}
if (index < items.length) {
setTimeout(processBatch, 0);
} else {
resolve();
}
}
processBatch();
});
}
6. Кастомный хук для параллелизма
function useParallelProcessing(data, processor, chunkSize = 50) {
const [result, setResult] = useState(null);
const [progress, setProgress] = useState(0);
const [isProcessing, setIsProcessing] = useState(false);
useEffect(() => {
if (!data || data.length === 0) return;
setIsProcessing(true);
let processed = 0;
const processChunk = (startIndex) => {
const endIndex = Math.min(startIndex + chunkSize, data.length);
const chunk = data.slice(startIndex, endIndex);
const chunkResult = chunk.map(processor);
processed += chunk.length;
setProgress((processed / data.length) * 100);
if (endIndex < data.length) {
requestIdleCallback(() => processChunk(endIndex));
} else {
setResult(chunkResult);
setIsProcessing(false);
}
};
requestIdleCallback(() => processChunk(0));
}, [data, processor, chunkSize]);
return { result, progress, isProcessing };
}
Сравнение подходов
Web Workers используй для CPU-intensive вычислений - они работают в отдельном потоке. Promise.resolve() подходит для микротасок. requestIdleCallback выполняет код только когда браузер свободен, не мешая анимациям. requestAnimationFrame синхронизирован с частотой монитора для гладких анимаций. setTimeout распределяет работу по времени простым способом.
Комбинируй эти подходы для оптимальной производительности: используй Workers для вычислений, requestAnimationFrame для анимаций, requestIdleCallback для фоновой работы, а Promise для быстрых операций.