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

Как возможно выполнение двух параллельных синхронных операций?

1.3 Junior🔥 131 комментариев
#JavaScript Core

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

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

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

Как возможно выполнение двух параллельных синхронных операций?

Это трёхчастный вопрос, который касается фундаментальных концепций параллелизма в JavaScript и браузере. Ответ зависит от того, что мы понимаем под "синхронными" и "параллельными" операциями.

Правда: В одном потоке JS параллелизма нет

JavaScript работает в одном потоке (single-threaded). Две синхронные операции просто не могут выполняться параллельно в одном потоке:

// Это ВСЕГДА выполнится последовательно, не параллельно
function operation1() {
  const start = Date.now();
  while (Date.now() - start < 2000) {} // 2 секунды
  console.log('Operation 1 done');
}

function operation2() {
  const start = Date.now();
  while (Date.now() - start < 2000) {} // 2 секунды
  console.log('Operation 2 done');
}

operation1(); // Заблокирует UI на 2 секунды
operation2(); // Начнёт выполняться только после operation1
// Итого: 4 секунды, UI заморожен

Решение 1: Web Workers (истинный параллелизм)

Для истинного параллелизма в браузере нужны Web Workers - они работают в отдельных потоках:

// main.js
const worker1 = new Worker('worker.js');
const worker2 = new Worker('worker.js');

// Запускаем две операции параллельно в разных потоках
worker1.postMessage({ operation: 'task1', duration: 2000 });
worker2.postMessage({ operation: 'task2', duration: 2000 });

worker1.onmessage = (e) => {
  console.log('Worker 1 done:', e.data);
};

worker2.onmessage = (e) => {
  console.log('Worker 2 done:', e.data);
};

// Обе операции выполняются параллельно!
// Время: ~2 секунды (параллельно), не 4 (последовательно)
// worker.js - отдельный поток
self.onmessage = (e) => {
  const { operation, duration } = e.data;
  
  // Синхронная операция в отдельном потоке
  const start = Date.now();
  while (Date.now() - start < duration) {}
  
  self.postMessage({ result: `${operation} completed` });
};

Решение 2: Асинхронность (квазипараллелизм)

Если операции асинхронные, они могут выглядеть как параллельные благодаря Event Loop:

// Асинхронные операции
function asyncOperation1() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('Operation 1 done');
      resolve();
    }, 2000);
  });
}

function asyncOperation2() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('Operation 2 done');
      resolve();
    }, 2000);
  });
}

// Запускаем параллельно
Promise.all([asyncOperation1(), asyncOperation2()]).then(() => {
  console.log('Both done');
});

// Вывод:
// Operation 1 done    (2сек)
// Operation 2 done    (2сек)
// Both done           (всего ~2сек, не 4!)

Это работает благодаря Event Loop:

console.log('Start');

setTimeout(() => console.log('Timeout 1'), 0);
setTimeout(() => console.log('Timeout 2'), 0);
fetch('/api/data1').then(() => console.log('Fetch 1 done'));
fetch('/api/data2').then(() => console.log('Fetch 2 done'));

console.log('End');

// Вывод:
// Start
// End
// Timeout 1 (микротаск очередь)
// Timeout 2
// Fetch 1 done (если оба завершились одновременно)
// Fetch 2 done

Решение 3: requestAnimationFrame (для графики)

Для операций, связанных с отрисовкой, можно использовать requestAnimationFrame для неблокирования:

let data1Processed = false;
let data2Processed = false;

function processLargeData1() {
  // Обрабатываем кусочек данных
  for (let i = 0; i < 10000; i++) {
    // Обработка
  }
  
  if (hasMoreData) {
    requestAnimationFrame(processLargeData1);
  } else {
    data1Processed = true;
    console.log('Data 1 processed');
  }
}

function processLargeData2() {
  // То же для данных 2
  for (let i = 0; i < 10000; i++) {
    // Обработка
  }
  
  if (hasMoreData) {
    requestAnimationFrame(processLargeData2);
  } else {
    data2Processed = true;
    console.log('Data 2 processed');
  }
}

// Запускаем обе операции
requestAnimationFrame(processLargeData1);
requestAnimationFrame(processLargeData2);

// UI остаётся отзывчивым, так как операции разбиты на кусочки

Решение 4: setTimeout для разбиения работы

Можно разбить синхронную работу на кусочки:

function heavyOperation1() {
  for (let i = 0; i < 1000000000; i++) {
    // Тяжёлые вычисления
  }
}

function heavyOperation2() {
  for (let i = 0; i < 1000000000; i++) {
    // Тяжёлые вычисления
  }
}

// Плохо: блокирует UI на 4 секунды
// heavyOperation1();
// heavyOperation2();

// Хорошо: разбиваем на части
function chunkWork(operation, chunkSize, onComplete) {
  let completed = 0;
  
  function doChunk() {
    for (let i = 0; i < chunkSize; i++) {
      operation();
      completed++;
    }
    
    if (completed < 1000000000 / chunkSize) {
      setTimeout(doChunk, 0);
    } else {
      onComplete();
    }
  }
  
  doChunk();
}

// Запускаем обе операции
chunkWork(heavyOperation1, 100000, () => console.log('Op1 done'));
chunkWork(heavyOperation2, 100000, () => console.log('Op2 done'));

// UI остаётся отзывчивым!

Решение 5: SharedArrayBuffer (для обмена данными между Workers)

Для коммуникации между потоками можно использовать SharedArrayBuffer:

// main.js
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);

const worker1 = new Worker('worker1.js');
const worker2 = new Worker('worker2.js');

worker1.postMessage({ buffer: sharedBuffer });
worker2.postMessage({ buffer: sharedBuffer });

// Оба worker'а могут параллельно писать и читать из одного буфера

Внимание: SharedArrayBuffer требует HTTPS и специальные заголовки COOP/COEP.

Таблица сравнения подходов

ПодходИстинный параллелизмБлокирует UIСложность
Синхронные операцииНетДаНизкая
Web WorkersДаНетСредняя
Promise/async-awaitНет (квази)НетСредняя
requestAnimationFrameНетНетСредняя
setTimeout chunksНетНетСредняя
SharedArrayBufferДаНетВысокая

Практический пример: Загрузка файлов параллельно

// Проблема: две загрузки файлов последовательно (неправильно)
async function uploadSequential() {
  const file1 = await upload('file1.txt');
  const file2 = await upload('file2.txt');
  console.log('Both done');
  // Время: время1 + время2
}

// Решение: загружаем параллельно (правильно)
async function uploadParallel() {
  const [file1, file2] = await Promise.all([
    upload('file1.txt'),
    upload('file2.txt')
  ]);
  console.log('Both done');
  // Время: max(время1, время2)
}

Краткое резюме

  1. Две синхронные операции в одном потоке - НЕВОЗМОЖНЫ параллельно
  2. Асинхронные операции - выглядят параллельными благодаря Event Loop
  3. Истинный параллелизм - нужны Web Workers (отдельные потоки)
  4. Не блокировать UI - используй async/await, setTimeout chunks или requestAnimationFrame
  5. Выбирай инструмент - зависит от типа операции (CPU-bound vs I/O-bound)

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

Как возможно выполнение двух параллельных синхронных операций? | PrepBro