实现一个 sleep 函数

手写一个 sleep 函数,实现异步等待指定毫秒数的功能

问题

在 JavaScript 中,我们经常需要让程序暂停一段时间后再继续执行。虽然 JavaScript 没有内置的 sleep 函数(不像其他语言如 Python),但我们可以利用 Promise 和 async/await 来实现一个类似的功能。

要求实现一个 sleep(ms) 函数,调用后能够让程序等待指定的毫秒数后再继续执行。

解答

/**
 * 实现 sleep 函数
 * @param {number} ms - 等待的毫秒数
 * @returns {Promise} 返回一个在指定时间后 resolve 的 Promise
 */
function sleep(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

// 方法二:使用 async/await 的工具函数
function sleep2(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 方法三:支持取消的 sleep
function sleepWithCancel(ms) {
  let timeoutId;
  const promise = new Promise((resolve, reject) => {
    timeoutId = setTimeout(resolve, ms);
  });
  
  // 添加取消方法
  promise.cancel = () => {
    clearTimeout(timeoutId);
  };
  
  return promise;
}

使用示例

// 示例1:基本使用
async function example1() {
  console.log('开始执行');
  await sleep(1000); // 等待 1 秒
  console.log('1秒后执行');
  await sleep(2000); // 等待 2 秒
  console.log('再过2秒后执行');
}

example1();

// 示例2:在循环中使用
async function example2() {
  for (let i = 1; i <= 5; i++) {
    console.log(`第 ${i} 次执行`);
    await sleep(1000); // 每次间隔 1 秒
  }
}

example2();

// 示例3:配合 Promise.then 使用
function example3() {
  console.log('开始');
  sleep(1000)
    .then(() => {
      console.log('1秒后');
      return sleep(1000);
    })
    .then(() => {
      console.log('2秒后');
    });
}

example3();

// 示例4:可取消的 sleep
async function example4() {
  console.log('开始等待');
  const sleepPromise = sleepWithCancel(5000);
  
  // 2秒后取消等待
  setTimeout(() => {
    sleepPromise.cancel();
    console.log('等待被取消');
  }, 2000);
  
  try {
    await sleepPromise;
    console.log('等待完成'); // 不会执行
  } catch (error) {
    console.log('等待被中断');
  }
}

example4();

// 示例5:模拟接口请求重试
async function fetchWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url);
      return response;
    } catch (error) {
      console.log(`第 ${i + 1} 次请求失败`);
      if (i < maxRetries - 1) {
        await sleep(1000 * (i + 1)); // 递增等待时间
      }
    }
  }
  throw new Error('请求失败');
}

关键点

  • Promise 封装:sleep 函数返回一个 Promise 对象,通过 setTimeout 在指定时间后调用 resolve,使 Promise 变为完成状态

  • async/await 配合:sleep 函数必须配合 await 关键字使用,才能实现真正的”等待”效果,否则只是返回一个 Promise 对象

  • 非阻塞特性:JavaScript 的 sleep 是非阻塞的,不会阻塞主线程,其他代码仍可以正常执行

  • 应用场景

    • 控制异步操作的执行节奏
    • 实现轮询间隔
    • 模拟网络延迟
    • 接口请求重试机制
  • 注意事项

    • sleep 只能在 async 函数中使用 await 调用
    • 不要在同步代码中期望 sleep 能阻塞执行
    • 如果需要取消等待,可以扩展实现支持取消的版本