← Назад к вопросам
Реализуйте функцию 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));
}
Как это работает
Принцип работы:
- Создаём промис — оборачиваем setTimeout в Promise
- Устанавливаем таймер — setTimeout вызывает resolve через ms миллисекунд
- Резолвим промис — 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));
}
// Не блокирует поток, асинхронно