← Назад к вопросам

Как организовать параллельное выполнение кода в браузере?

2.0 Middle🔥 131 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Параллельное выполнение кода в браузере

Браузер работает на базе 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 для быстрых операций.