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

Реализуйте функцию sleep на промисах

1.0 Junior🔥 151 комментариев
#Node.js и JavaScript

Условие

Напишите функцию sleep(ms), которая возвращает промис, резолвящийся через указанное количество миллисекунд. Функция должна работать с async/await:

function sleep(ms) {
  // Ваш код
}

// Пример использования:
async function demo() {
  console.log("Start");
  await sleep(2000);
  console.log("After 2 seconds");
  await sleep(1000);
  console.log("After 1 more second");
}

demo();

Что проверяется

  • Понимание промисов
  • Работа с async/await
  • Создание промисов с setTimeout

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

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

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

Решение

Базовая реализация

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Как это работает

Принцип работы:

  1. Создаём промис — оборачиваем setTimeout в Promise
  2. Устанавливаем таймер — setTimeout вызывает resolve через ms миллисекунд
  3. Резолвим промис — resolve() выполнит await в async функции

Пошаговый процесс:

Старт: sleep(2000)
├─ Создан новый Promise
├─ setTimeout установлен на 2000мс
├─ Возвращаем промис
└─ При await управление передаётся дальше

Через 2 сек:
├─ setTimeout срабатывает
├─ resolve() выполняется
├─ Промис резолвится
└─ await разблокируется, код продолжается

Пример использования

async function demo() {
  console.log("Start");
  await sleep(2000);
  console.log("After 2 seconds");
  await sleep(1000);
  console.log("After 1 more second");
}

demo();

// Вывод:
// Start (сразу)
// After 2 seconds (через 2 сек)
// After 1 more second (через 3 сек)

Версия с возвращаемым значением

function sleep<T>(ms: number, value?: T): Promise<T | undefined> {
  return new Promise(resolve => setTimeout(() => resolve(value), ms));
}

// Пример:
const result = await sleep(1000, "Hello");
console.log(result); // Hello

Версия с отменой (AbortSignal)

function sleep(ms: number, signal?: AbortSignal): Promise<void> {
  return new Promise((resolve, reject) => {
    const timeoutId = setTimeout(resolve, ms);

    if (signal) {
      signal.addEventListener('abort', () => {
        clearTimeout(timeoutId);
        reject(new Error('Sleep cancelled'));
      });
    }
  });
}

// Пример использования:
const controller = new AbortController();

setTimeout(() => {
  controller.abort(); // Отменяем sleep
}, 500);

try {
  await sleep(2000, controller.signal);
} catch (error) {
  console.error('Sleep was cancelled');
}

Практический пример: Retry с задержкой

async function retryWithDelay<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3,
  delayMs: number = 1000
): Promise<T> {
  let lastError: Error | null = null;

  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;
      console.log(`Attempt ${i + 1} failed, retrying in ${delayMs}ms...`);
      await sleep(delayMs);
    }
  }

  throw lastError || new Error('All retries failed');
}

// Использование:
const apiCall = async () => {
  const response = await fetch('/api/data');
  if (!response.ok) throw new Error('Failed');
  return response.json();
};

const data = await retryWithDelay(apiCall, 3, 1000);

Пример: Последовательная обработка с задержками

async function processItemsWithDelay<T>(
  items: T[],
  callback: (item: T) => Promise<void>,
  delayMs: number = 1000
): Promise<void> {
  for (let i = 0; i < items.length; i++) {
    await callback(items[i]);
    if (i < items.length - 1) {
      await sleep(delayMs);
    }
  }
}

// Использование:
const users = [1, 2, 3, 4, 5];
const processUser = async (id: number) => {
  console.log(`Processing user ${id}`);
  // Имитация API запроса
  await fetch(`/api/users/${id}`);
};

await processItemsWithDelay(users, processUser, 500);
// Processing user 1 → (0.5s) → Processing user 2 → ... и т.д.

Пример: Polling с таймаутом

async function poll<T>(
  fn: () => Promise<T | null>,
  maxAttempts: number = 10,
  delayMs: number = 1000
): Promise<T> {
  for (let i = 0; i < maxAttempts; i++) {
    const result = await fn();
    if (result !== null) {
      return result;
    }
    if (i < maxAttempts - 1) {
      await sleep(delayMs);
    }
  }

  throw new Error('Polling timeout');
}

// Использование: ждём пока статус изменится
const checkStatus = async () => {
  const response = await fetch('/api/status');
  const { status } = await response.json();
  return status === 'ready' ? status : null;
};

const status = await poll(checkStatus, 10, 500);
console.log(`Got status: ${status}`);

Версия с экспоненциальной задержкой

async function exponentialBackoff<T>(
  fn: () => Promise<T>,
  maxRetries: number = 5,
  initialDelayMs: number = 100
): Promise<T> {
  let delay = initialDelayMs;

  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      console.log(`Attempt ${i + 1} failed, waiting ${delay}ms...`);
      await sleep(delay);
      delay *= 2; // Экспоненциальный рост
    }
  }

  throw new Error('All retries failed');
}

// Использование:
await exponentialBackoff(
  () => fetch('/api/data').then(r => r.json()),
  5,
  100
);
// 100ms → 200ms → 400ms → 800ms → 1600ms

Тестирование

import { describe, it, expect, vi, beforeEach } from 'vitest';

describe('sleep', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  it('должен резолвить промис через указанное время', async () => {
    const fn = vi.fn();
    const promise = sleep(1000).then(fn);

    expect(fn).not.toHaveBeenCalled();

    vi.advanceTimersByTime(999);
    await Promise.resolve();
    expect(fn).not.toHaveBeenCalled();

    vi.advanceTimersByTime(1);
    await Promise.resolve();
    expect(fn).toHaveBeenCalledOnce();
  });

  it('должен работать с async/await', async () => {
    const startTime = Date.now();
    await sleep(2000);
    const elapsed = Date.now() - startTime;

    // В fake timers это выполнится мгновенно
    expect(elapsed).toBeLessThan(50);
  });

  it('должен поддерживать цепочку вызовов', async () => {
    const fn = vi.fn();

    sleep(1000)
      .then(() => {
        fn('first');
        return sleep(500);
      })
      .then(() => fn('second'));

    vi.advanceTimersByTime(1500);
    await Promise.resolve();
    expect(fn).toHaveBeenCalledTimes(2);
  });

  it('должен работать с Promise.all', async () => {
    const promises = [
      sleep(1000).then(() => 'a'),
      sleep(2000).then(() => 'b'),
      sleep(500).then(() => 'c')
    ];

    const promise = Promise.all(promises);
    vi.advanceTimersByTime(2000);
    const results = await promise;

    expect(results).toEqual(['a', 'b', 'c']);
  });
});

Сравнение с другими подходами

Версия 1: С resolve

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Версия 2: С явной резолвацией

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => {
    setTimeout(() => resolve(), ms);
  });
}

Версия 3: Со значением

function sleep(ms: number): Promise<number> {
  return new Promise(resolve => setTimeout(() => resolve(ms), ms));
}

Первая версия — самая лаконичная и предпочтительная.

Практические применения

1. Простая задержка:

await sleep(1000);
console.log('1 секунда прошла');

2. Retry логика:

for (let i = 0; i < 3; i++) {
  try {
    return await fetchData();
  } catch (e) {
    if (i < 2) await sleep(1000);
  }
}

3. Rate limiting:

for (const item of items) {
  await processItem(item);
  await sleep(100); // 100ms между запросами
}

4. Имитация загрузки:

function* generateNumbers() {
  for (let i = 0; i < 5; i++) {
    yield i;
  }
}

for (const num of generateNumbers()) {
  console.log(num);
  await sleep(500);
}

Ошибки, которых нужно избегать

❌ НЕПРАВИЛЬНО:

// Это не promisified sleep!
function sleep(ms: number) {
  const start = Date.now();
  while (Date.now() - start < ms) {}
}
// Блокирует весь поток!

✅ ПРАВИЛЬНО:

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}
// Не блокирует поток, асинхронно
Реализуйте функцию sleep на промисах | PrepBro