鲁斯前端布鲁斯前端

文章中英模式

布鲁斯前端JS面试题目 - 实现 Promise Sleep 函数

学习如何实现 JavaScript 中的 Promise Sleep 函数,处理延迟执行、定时任务与动画,掌握异步程序设计的基础工具。

影片縮圖

懒得看文章?那就来看视频吧

Promise Sleep 函数概述

Promise Sleep 函数是一个简单但实用的工具,它能让代码暂停执行指定的时间。在各种异步操作中,sleep 函数非常有用,尤其是需要延迟执行、模拟网络延迟或控制动画节奏时。

为什么需要 Sleep 函数?

  • 1. 控制执行节奏:在连续操作之间添加延迟,避免过度频繁的操作
  • 2. 模拟网络延迟:测试代码对延迟的处理能力
  • 3. 动画与视觉效果:控制连续视觉变化的间隔
  • 4. API 限流:避免短时间内发送过多请求
  • 5. 重试机制:实现指数退避等重试策略

基本 Promise Sleep 实现

下面是一个简单的 Promise Sleep 函数实现:

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

// 使用示例
async function example() {
  console.log('开始');
  
  await sleep(2000); // 暂停 2 秒
  
  console.log('2秒后'); // 2秒后执行
}

example();

这个函数创建了一个新的 Promise,在指定的毫秒数后调用 resolve。结合 async/await 语法,可以让代码暂停执行指定的时间。

进阶 Promise Sleep 功能

我们可以扩展基本的 sleep 函数,增加更多功能:

带返回值的 Sleep

function sleepWithValue(ms, value) {
  return new Promise(resolve => setTimeout(() => resolve(value), ms));
}

// 使用范例
async function example() {
  console.log('开始');
  
  const result = await sleepWithValue(2000, '延迟返回的数据');
  
  console.log(result); // 2秒后显示:延迟返回的数据
}

example();

可取消的 Sleep

function cancellableSleep(ms) {
  let timeoutId;
  
  const promise = new Promise(resolve => {
    timeoutId = setTimeout(resolve, ms);
  });
  
  // 添加取消方法
  promise.cancel = function() {
    clearTimeout(timeoutId);
  };
  
  return promise;
}

// 使用示例
async function example() {
  console.log('开始');
  
  const sleepPromise = cancellableSleep(5000);
  
  // 2秒后取消休眠
  setTimeout(() => {
    console.log('取消休眠');
    sleepPromise.cancel();
  }, 2000);
  
  try {
    await sleepPromise;
    console.log('5秒后'); // 不会执行,因为已取消
  } catch (error) {
    console.log('Sleep 被取消');
  }
}

example();

实际应用场景

以下是一些 Promise Sleep 函数的实际应用:

1. 实现 API 请求重试机制

async function fetchWithRetry(url, options = {}, maxRetries = 3) {
  const { retryDelay = 1000 } = options;
  
  let lastError;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      // 尝试发起请求
      const response = await fetch(url, options);
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      return await response.json();
    } catch (error) {
      console.log(`尝试 ${attempt + 1} 失败: ${error.message}`);
      lastError = error;
      
      if (attempt < maxRetries - 1) {
        // 使用指数退避策略计算下次重试延迟
        const delay = retryDelay * Math.pow(2, attempt);
        console.log(`等待 ${delay}ms 后重试...`);
        await sleep(delay);
      }
    }
  }
  
  throw lastError;
}

// 使用示例
async function getData() {
  try {
    const data = await fetchWithRetry('https://api.example.com/data');
    console.log('成功获取数据:', data);
  } catch (error) {
    console.error('所有重试都失败了:', error);
  }
}

getData();

2. 创建动画序列

async function animateSequence(element) {
  // 设置初始状态
  element.style.opacity = '0';
  element.style.transform = 'translateY(20px)';
  element.style.transition = 'all 0.5s ease-out';
  
  // 确保元素可见
  element.style.display = 'block';
  
  // 等待一帧以确保过渡效果生效
  await sleep(50);
  
  // 淡入动画
  element.style.opacity = '1';
  element.style.transform = 'translateY(0)';
  
  // 等待淡入完成
  await sleep(500);
  
  // 添加强调效果
  element.style.transform = 'scale(1.1)';
  await sleep(200);
  
  // 恢复正常大小
  element.style.transform = 'scale(1)';
}

// 使用示例
document.getElementById('showButton').addEventListener('click', () => {
  const notification = document.getElementById('notification');
  animateSequence(notification);
});

3. 限流 API 请求

class RateLimiter {
  constructor(requestsPerSecond) {
    this.minDelayMs = 1000 / requestsPerSecond;
    this.lastRequestTime = 0;
  }
  
  async limit() {
    const now = Date.now();
    const timeElapsed = now - this.lastRequestTime;
    
    if (timeElapsed < this.minDelayMs) {
      // 计算需要等待的时间
      const waitTime = this.minDelayMs - timeElapsed;
      await sleep(waitTime);
    }
    
    this.lastRequestTime = Date.now();
  }
}

// 使用示例 - 每秒最多发送2个请求
const limiter = new RateLimiter(2);

async function fetchWithRateLimit(url) {
  await limiter.limit();
  return fetch(url).then(res => res.json());
}

// 示范多次调用
async function fetchMultipleData() {
  console.log('开始获取数据...');
  
  // 这些请求将被限流,每秒最多执行2个
  const results = await Promise.all([
    fetchWithRateLimit('https://api.example.com/1'),
    fetchWithRateLimit('https://api.example.com/2'),
    fetchWithRateLimit('https://api.example.com/3'),
    fetchWithRateLimit('https://api.example.com/4')
  ]);
  
  console.log('所有数据获取完成', results);
}

fetchMultipleData();