浏览器端异步通信协议选择

Adam651 +0/-0 0 0 正常 2025-12-24T07:01:19 JavaScript · 异步编程 · promises

在现代浏览器环境中,选择合适的异步通信协议对应用性能和用户体验至关重要。本文将通过实际代码示例展示如何根据不同场景选择最适合的异步通信方案。

XMLHttpRequest vs Fetch API

首先对比传统的XMLHttpRequest与现代的Fetch API。XMLHttpRequest虽然兼容性好,但语法复杂,而Fetch API更简洁且支持Promise链式调用。

// 使用XMLHttpRequest
function fetchWithXHR(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.responseText);
      } else {
        reject(new Error(`HTTP ${xhr.status}`));
      }
    };
    xhr.onerror = () => reject(new Error('Network error'));
    xhr.send();
  });
}

// 使用Fetch API
async function fetchWithFetch(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error('Fetch error:', error);
    throw error;
  }
}

实际应用:构建智能通信选择器

在实际项目中,我们可以通过检测浏览器支持情况来动态选择最优方案:

class AsyncCommunicator {
  constructor() {
    this.supportsFetch = typeof fetch !== 'undefined';
    this.supportsAbortController = typeof AbortController !== 'undefined';
  }

  async request(url, options = {}) {
    // 优先使用Fetch API
    if (this.supportsFetch) {
      return await this.fetchRequest(url, options);
    }
    
    // 回退到XMLHttpRequest
    return await this.xhrRequest(url, options);
  }

  async fetchRequest(url, options) {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时
    
    try {
      const response = await fetch(url, {
        ...options,
        signal: controller.signal
      });
      clearTimeout(timeoutId);
      return await response.json();
    } catch (error) {
      clearTimeout(timeoutId);
      if (error.name === 'AbortError') {
        throw new Error('Request timeout');
      }
      throw error;
    }
  }

  xhrRequest(url, options) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open(options.method || 'GET', url);
      
      // 设置请求头
      Object.entries(options.headers || {}).forEach(([key, value]) => {
        xhr.setRequestHeader(key, value);
      });
      
      xhr.onload = () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          try {
            resolve(JSON.parse(xhr.responseText));
          } catch (e) {
            resolve(xhr.responseText);
          }
        } else {
          reject(new Error(`HTTP ${xhr.status}`));
        }
      };
      
      xhr.onerror = () => reject(new Error('Network error'));
      xhr.send(options.body || null);
    });
  }
}

// 使用示例
const communicator = new AsyncCommunicator();
communicator.request('/api/data', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ key: 'value' })
}).then(data => {
  console.log('Success:', data);
}).catch(error => {
  console.error('Error:', error);
});

WebSocket vs HTTP长轮询

对于实时通信场景,需要选择WebSocket协议:

class WebSocketClient {
  constructor(url) {
    this.url = url;
    this.socket = null;
    this.reconnectAttempts = 0;
    this.maxReconnectAttempts = 5;
  }

  connect() {
    return new Promise((resolve, reject) => {
      try {
        this.socket = new WebSocket(this.url);
        
        this.socket.onopen = () => {
          console.log('WebSocket connected');
          this.reconnectAttempts = 0;
          resolve();
        };

        this.socket.onmessage = (event) => {
          const data = JSON.parse(event.data);
          // 处理接收到的数据
          this.handleMessage(data);
        };

        this.socket.onerror = (error) => {
          console.error('WebSocket error:', error);
          reject(error);
        };

        this.socket.onclose = () => {
          if (this.reconnectAttempts < this.maxReconnectAttempts) {
            setTimeout(() => {
              this.reconnectAttempts++;
              this.connect();
            }, 1000 * Math.pow(2, this.reconnectAttempts));
          }
        };
      } catch (error) {
        reject(error);
      }
    });
  }

  sendMessage(message) {
    if (this.socket && this.socket.readyState === WebSocket.OPEN) {
      this.socket.send(JSON.stringify(message));
    }
  }

  handleMessage(data) {
    // 子类实现具体的消息处理逻辑
    console.log('Received:', data);
  }
}

总结

选择异步通信协议时应考虑:浏览器兼容性、性能需求、实时性要求和错误处理策略。通过构建智能的通信选择器,可以在不同环境下自动选择最优方案,确保应用的稳定性和用户体验。

推广
广告位招租

讨论

0/2000