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

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

1.8 Middle🔥 151 комментариев
#JavaScript Core

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

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

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

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

Этот вопрос можно интерпретировать по-разному: заблокировать выполнение асинхронного кода, остановить цикл, отменить операцию или контролировать выполнение. Рассмотрим все основные подходы.

Способ 1: Async/Await — ожидание перед продолжением

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

// Без блокировки — код продолжает выполняться
fetch('/api/data');
console.log('Request sent'); // Выполнится сразу

// С блокировкой — ждем результата
await fetch('/api/data');
console.log('Data received'); // Выполнится только после ответа

Практический пример:

async function processUser() {
  // Блокируем выполнение до загрузки пользователя
  const response = await fetch('/api/user/123');
  const user = await response.json();
  
  // Код здесь выполнится только после получения данных
  console.log('User:', user);
  return user;
}

Способ 2: Promise.all() для ожидания нескольких операций

// Блокируем до завершения всех операций
const [users, posts, comments] = await Promise.all([
  fetch('/api/users').then(r => r.json()),
  fetch('/api/posts').then(r => r.json()),
  fetch('/api/comments').then(r => r.json())
]);

// Код выполнится только когда все три запроса завершены
console.log('All data loaded');

Способ 3: AbortController — отменить операцию

AbortController позволяет отменить асинхронную операцию:

const controller = new AbortController();

// Отправляем запрос с сигналом отмены
const fetchPromise = fetch('/api/data', {
  signal: controller.signal
});

// Отменяем запрос через 5 секунд
setTimeout(() => controller.abort(), 5000);

try {
  const response = await fetchPromise;
  const data = await response.json();
  console.log('Data:', data);
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Request was cancelled');
  } else {
    console.error('Error:', error);
  }
}

Практический пример: отмена при удалении компонента (React):

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const controllerRef = useRef(null);

  useEffect(() => {
    // Создаем AbortController для этого запроса
    const controller = new AbortController();
    controllerRef.current = controller;

    const fetchUser = async () => {
      try {
        const response = await fetch(`/api/users/${userId}`, {
          signal: controller.signal
        });
        const data = await response.json();
        setUser(data);
      } catch (error) {
        if (error.name !== 'AbortError') {
          console.error('Error:', error);
        }
      }
    };

    fetchUser();

    // При удалении компонента отменяем запрос
    return () => controller.abort();
  }, [userId]);

  return user ? <div>{user.name}</div> : <div>Loading...</div>;
}

Способ 4: Condition flags — контролируемое выполнение

let shouldContinue = true;

async function longRunningTask() {
  for (let i = 0; i < 1000000; i++) {
    if (!shouldContinue) {
      console.log('Task cancelled');
      break; // Выход из цикла
    }
    
    // Heavy computation
    doHeavyWork(i);
  }
}

// Запустить задачу
longRunningTask();

// Остановить, когда нужно
shouldContinue = false;

Способ 5: Semaphore / Mutex — синхронизация доступа

Блокировка для ограничения одновременного доступа:

class Semaphore {
  constructor(maxConcurrent = 1) {
    this.maxConcurrent = maxConcurrent;
    this.current = 0;
    this.queue = [];
  }

  async acquire() {
    if (this.current < this.maxConcurrent) {
      this.current++;
      return;
    }

    // Ждем, пока кто-то освободит место
    return new Promise(resolve => {
      this.queue.push(resolve);
    });
  }

  release() {
    this.current--;
    const resolve = this.queue.shift();
    if (resolve) {
      this.current++;
      resolve();
    }
  }
}

// Использование: максимум 2 одновременных запроса
const semaphore = new Semaphore(2);

async function fetchWithLimit(url) {
  await semaphore.acquire();
  try {
    const response = await fetch(url);
    return response.json();
  } finally {
    semaphore.release();
  }
}

// Запускаем 5 запросов, но только 2 будут одновременно
Promise.all([
  fetchWithLimit('/api/1'),
  fetchWithLimit('/api/2'),
  fetchWithLimit('/api/3'),
  fetchWithLimit('/api/4'),
  fetchWithLimit('/api/5')
]);

Способ 6: Busy waiting (не рекомендуется!)

// ❌ ПЛОХО — заморозит UI
function blockMainThread() {
  const end = Date.now() + 5000;
  while (Date.now() < end) {
    // Активно занимаем процессор
  }
  console.log('Done');
}

// ✅ ХОРОШО — используй setTimeout
function blockMainThreadCorrectly() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('Done');
      resolve();
    }, 5000);
  });
}

Способ 7: Generator функции для контролируемого выполнения

function* generatorTask() {
  console.log('Step 1');
  yield; // Пауза
  
  console.log('Step 2');
  yield; // Пауза
  
  console.log('Step 3');
  yield; // Пауза
  
  console.log('Done');
}

const gen = generatorTask();

gen.next(); // Step 1
gen.next(); // Step 2
gen.next(); // Step 3
gen.next(); // Done

// Или с циклом:
for (const _ of generatorTask()) {
  // Выполняется пошагово
}

Способ 8: Lock (с использованием Web Locks API)

// Современный способ заблокировать ресурс
navigator.locks.request('myResource', async lock => {
  console.log('Lock acquired');
  
  // Этот код выполняется с исключительным доступом
  await doSomething();
  
  console.log('Lock released');
}); // Блокировка автоматически освобождается

Практический пример: Rate Limiting

class RateLimiter {
  constructor(maxRequests, windowMs) {
    this.maxRequests = maxRequests;
    this.windowMs = windowMs;
    this.requests = [];
  }

  async acquire() {
    const now = Date.now();
    
    // Удаляем старые запросы (старше окна)
    this.requests = this.requests.filter(
      timestamp => now - timestamp < this.windowMs
    );

    // Если достигли лимита, ждем
    if (this.requests.length >= this.maxRequests) {
      const oldestRequest = this.requests[0];
      const waitTime = this.windowMs - (now - oldestRequest);
      
      console.log(`Rate limit exceeded. Waiting ${waitTime}ms`);
      await new Promise(resolve => setTimeout(resolve, waitTime));
      
      return this.acquire(); // Повторная попытка
    }

    // Добавляем текущий запрос
    this.requests.push(now);
  }
}

// Использование: максимум 5 запросов в 10 секунд
const limiter = new RateLimiter(5, 10000);

async function fetchWithRateLimit(url) {
  await limiter.acquire();
  return fetch(url);
}

// Запускаем 10 запросов, но выполнится в 2 батча
for (let i = 0; i < 10; i++) {
  fetchWithRateLimit(`/api/${i}`);
}

Способ 9: Condition variables (для сложной синхронизации)

class ConditionVariable {
  constructor() {
    this.waiters = [];
  }

  async wait() {
    return new Promise(resolve => {
      this.waiters.push(resolve);
    });
  }

  notify() {
    const resolve = this.waiters.shift();
    if (resolve) resolve();
  }

  notifyAll() {
    while (this.waiters.length > 0) {
      this.notify();
    }
  }
}

// Пример: Producer-Consumer
const queue = [];
const condition = new ConditionVariable();
const MAX_SIZE = 5;

async function producer(item) {
  while (queue.length >= MAX_SIZE) {
    console.log('Queue full, waiting...');
    await condition.wait();
  }
  queue.push(item);
  console.log('Produced:', item);
}

async function consumer() {
  while (queue.length === 0) {
    console.log('Queue empty, waiting...');
    await condition.wait();
  }
  const item = queue.shift();
  console.log('Consumed:', item);
  condition.notifyAll(); // Разбудить producer'ов
}

Способ 10: setTimeout для отложенного выполнения

// Блокируем на 5 секунд
setTimeout(() => {
  console.log('Executed after 5 seconds');
}, 5000);

// С Promise
await new Promise(resolve => setTimeout(resolve, 5000));
console.log('Done blocking');

Сравнение подходов

// 1. await (асинхронное ожидание) — рекомендуется
await asyncOperation();

// 2. AbortController (отмена операции) — для длительных задач
const controller = new AbortController();
await fetch(url, { signal: controller.signal });

// 3. Promise.all() — ожидание нескольких операций
await Promise.all([op1, op2, op3]);

// 4. Flag + условия — контролируемое выполнение
if (shouldContinue) { /* ... */ }

// 5. Semaphore — ограничение одновременного доступа
await semaphore.acquire();

// 6. setTimeout — отложенное выполнение
await new Promise(r => setTimeout(r, ms));

Заключение

Как заблокировать код в JavaScript:

  1. await — дождись асинхронной операции
  2. Promise.all() — жди несколько операций
  3. AbortController — отмени длительную операцию
  4. Flag + условия — контролируемое выполнение
  5. Semaphore/Mutex — синхронизация доступа

Важно помнить:

  • Не блокируй main thread (UI зависнет)
  • Используй асинхронный код (async/await)
  • Отменяй ненужные операции (AbortController)
  • Предоставляй feedback пользователю (loading state)