JavaScript Promise 유틸리티

비동기 프로그래밍을 위한 재시도(retry), 타임아웃(timeout), 동시성 제어(pool), 디바운스(debounce) 등 유용한 Promise 패턴 모음입니다.

Gist
/**
 * 지정된 시간만큼 대기합니다.
 */
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

/**
 * Promise에 타임아웃을 적용합니다.
 */
const withTimeout = (promise, ms) => {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error(`Operation timed out after ${ms}ms`)), ms)
  );
  return Promise.race([promise, timeout]);
};

/**
 * 실패 시 지정된 횟수만큼 재시도합니다.
 */
const retry = async (fn, retries = 3, interval = 1000) => {
  try {
    return await fn();
  } catch (error) {
    if (retries <= 0) throw error;
    await delay(interval);
    return retry(fn, retries - 1, interval);
  }
};

/**
 * 비동기 함수의 동시 실행 수를 제한합니다.
 */
const promisePool = async (tasks, concurrency) => {
  const results = [];
  const executing = new Set();

  for (const task of tasks) {
    const p = Promise.resolve().then(() => task());
    results.push(p);
    executing.add(p);

    const clean = () => executing.delete(p);
    p.then(clean).catch(clean);

    if (executing.size >= concurrency) {
      await Promise.race(executing);
    }
  }

  return Promise.all(results);
};

/**
 * 비동기 디바운스 함수입니다.
 */
const debounceAsync = (fn, ms) => {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    return new Promise((resolve) => {
      timeout = setTimeout(() => resolve(fn(...args)), ms);
    });
  };
};

// 사용 예시
const fetchData = () => fetch('https://api.example.com/data').then(res => res.json());

(async () => {
  try {
    const data = await retry(() => withTimeout(fetchData(), 5000), 3);
    console.log(data);
  } catch (err) {
    console.error('Final error:', err.message);
  }
})();