鲁斯前端布鲁斯前端

文章中英模式

布鲁斯前端JS面试题目 - 实现 Promise 超时控制

学习如何实现 Promise 超时控制机制,掌握异步任务的时间管理技巧,提升前端应用稳定性与面试竞争力。

影片縮圖

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

Promise 超时控制概述

在实际开发中,我们经常需要为异步操作设定超时限制,避免长时间等待或无限阻塞。Promise 超时控制是一种重要的技术,可以确保应用程序的响应性和稳定性。

超时控制的重要性

  • 1. 防止无限等待:避免 Promise 永远处于 pending 状态
  • 2. 提升用户体验:及时响应用户操作,避免界面冻结
  • 3. 资源管理:防止资源泄漏和系统过载
  • 4. 错误处理:提供明确的超时错误信息

在前端面试中,实现 Promise 超时控制是测试候选人对异步处理、错误处理和时间管理理解的重要题目。

基本实现:Promise 超时控制

以下是一个基本的 Promise 超时控制实现:

// 基本 Promise 超时控制函数
function timeoutPromise(promise, timeout) {
  return Promise.race([
    promise,
    new Promise((_, reject) => {
      setTimeout(() => {
        reject(new Error(`操作超时 (${timeout}ms)`));
      }, timeout);
    })
  ]);
}

// 使用示例
const fetchWithTimeout = (url, timeout = 5000) => {
  return timeoutPromise(
    fetch(url),
    timeout
  );
};

// 测试超时控制
fetchWithTimeout('https://api.example.com/data', 3000)
  .then(response => response.json())
  .then(data => {
    console.log('数据获取成功:', data);
  })
  .catch(error => {
    if (error.message.includes('超时')) {
      console.error('请求超时,请稍后重试');
    } else {
      console.error('请求失败:', error);
    }
  });

进阶实现:可取消的 Promise

除了超时控制,我们还可以实现可取消的 Promise,提供更灵活的异步操作控制:

// 可取消的 Promise 实现
function cancellablePromise(promise) {
  let isCancelled = false;
  
  const wrappedPromise = new Promise((resolve, reject) => {
    promise
      .then(result => {
        if (!isCancelled) {
          resolve(result);
        }
      })
      .catch(error => {
        if (!isCancelled) {
          reject(error);
        }
      });
  });
  
  // 返回 Promise 和取消函数
  return {
    promise: wrappedPromise,
    cancel: () => {
      isCancelled = true;
    }
  };
}

// 结合超时和取消功能
function timeoutCancellablePromise(promise, timeout) {
  const { promise: wrappedPromise, cancel } = cancellablePromise(promise);
  
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => {
      cancel();
      reject(new Error(`操作超时 (${timeout}ms)`));
    }, timeout);
  });
  
  return Promise.race([wrappedPromise, timeoutPromise]);
}

// 使用示例
const longRunningTask = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('任务完成');
    }, 10000); // 10秒
  });
};

// 设定 5 秒超时
timeoutCancellablePromise(longRunningTask(), 5000)
  .then(result => {
    console.log('任务成功:', result);
  })
  .catch(error => {
    console.error('任务失败:', error.message);
  });

实际应用场景

Promise 超时控制在实际开发中有很多应用场景:

1. API 请求超时控制

// API 请求超时控制
class ApiClient {
  constructor(baseURL, defaultTimeout = 10000) {
    this.baseURL = baseURL;
    this.defaultTimeout = defaultTimeout;
  }
  
  async request(endpoint, options = {}) {
    const { timeout = this.defaultTimeout, ...fetchOptions } = options;
    
    const url = `${this.baseURL}${endpoint}`;
    
    try {
      const response = await timeoutPromise(
        fetch(url, fetchOptions),
        timeout
      );
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
      
      return await response.json();
    } catch (error) {
      if (error.message.includes('超时')) {
        throw new Error('请求超时,请检查网络连接');
      }
      throw error;
    }
  }
  
  get(endpoint, timeout) {
    return this.request(endpoint, { timeout });
  }
  
  post(endpoint, data, timeout) {
    return this.request(endpoint, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
      timeout
    });
  }
}

// 使用示例
const apiClient = new ApiClient('https://api.example.com', 5000);

apiClient.get('/users', 3000)
  .then(users => {
    console.log('用户列表:', users);
  })
  .catch(error => {
    console.error('获取用户失败:', error.message);
  });

2. 用户操作超时处理

// 用户操作超时处理
class UserActionManager {
  constructor() {
    this.pendingActions = new Map();
  }
  
  // 执行用户操作,设定超时
  async executeAction(actionId, actionPromise, timeout = 30000) {
    // 如果已有相同操作在执行,先取消
    if (this.pendingActions.has(actionId)) {
      this.cancelAction(actionId);
    }
    
    const { promise, cancel } = cancellablePromise(actionPromise);
    
    // 存储取消函数
    this.pendingActions.set(actionId, cancel);
    
    try {
      const result = await timeoutCancellablePromise(promise, timeout);
      this.pendingActions.delete(actionId);
      return result;
    } catch (error) {
      this.pendingActions.delete(actionId);
      
      if (error.message.includes('超时')) {
        // 显示用户友好的超时提示
        this.showTimeoutMessage(actionId);
      }
      
      throw error;
    }
  }
  
  // 取消特定操作
  cancelAction(actionId) {
    const cancel = this.pendingActions.get(actionId);
    if (cancel) {
      cancel();
      this.pendingActions.delete(actionId);
    }
  }
  
  // 取消所有操作
  cancelAllActions() {
    this.pendingActions.forEach(cancel => cancel());
    this.pendingActions.clear();
  }
  
  // 显示超时提示
  showTimeoutMessage(actionId) {
    console.warn(`操作 ${actionId} 超时,请稍后重试`);
    // 这里可以显示 UI 提示
  }
}

// 使用示例
const actionManager = new UserActionManager();

// 模拟长时间操作
const longOperation = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('操作完成');
    }, 5000);
  });
};

// 执行操作,设定 3 秒超时
actionManager.executeAction('upload-file', longOperation(), 3000)
  .then(result => {
    console.log('操作成功:', result);
  })
  .catch(error => {
    console.error('操作失败:', error.message);
  });