Skip to content

轮询

前端轮询一般用于定期向服务器请求数据,以获取最新状态。实现轮询的方式有以下几种:


1. setInterval 轮询(基础方式)

最常见的方法是使用 setInterval 定时请求:

javascript
function fetchData() {
  fetch('/api/data')
    .then(response => response.json())
    .then(data => console.log('获取的数据:', data))
    .catch(error => console.error('获取失败', error));
}

// 每5秒请求一次
setInterval(fetchData, 5000);
  • 优点:实现简单
  • 缺点
    • 固定时间间隔,可能会造成服务器压力
    • 即使数据没有变化也会继续请求
    • 受网络或者服务器处理速度的限制,不能保证服务器请求按时返回

2. setTimeout 递归轮询(更优方式)

相比 setIntervalsetTimeout 方式更灵活,可以在上次请求完成后再等待一段时间:

setTimeout 不会阻塞主线程,每次执行 fetchData() 后,调用栈都会清空,不会堆积在栈

javascript
function fetchData() {
  fetch('/api/data')
    .then(response => response.json())
    .then(data => {
      console.log('获取的数据:', data);
      // 等待 5 秒后再执行下一次请求
      setTimeout(fetchData, 5000);
    })
    .catch(error => {
      console.error('获取失败', error);
      setTimeout(fetchData, 5000); // 发生错误也继续轮询
    });
}

fetchData(); // 启动轮询
  • 优点
    • 只有在上次请求完成后才会进行下一次请求,避免请求堆积
    • 可根据请求结果动态调整下一次请求的时间
  • 缺点
    • 需要手动管理轮询的启动与停止

3. 使用 async/await 实现递归轮询

可以使用 async/await 让代码更加清晰:

javascript
async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    console.log('获取的数据:', data);
  } catch (error) {
    console.error('获取失败', error);
  }

  // 等待 5 秒后继续轮询
  setTimeout(fetchData, 5000);
}

fetchData();
  • 优点
    • 代码可读性更高
    • 只在请求完成后才发起下一次请求

4. 使用 AbortController 取消轮询

AbortController原生 JavaScript 提供的 API,用于控制 可中止的异步操作,如 fetch 请求。它可以:

  • 取消正在进行的 fetch 请求,避免不必要的网络请求
  • 结合轮询机制,随时手动终止轮询
  • 提高性能,减少网络开销

有时候我们需要在某些情况下停止轮询,比如用户离开页面或满足某个条件:

javascript
let controller = new AbortController();
let isPolling = true; // 控制轮询是否继续

async function fetchData() {
  if (!isPolling) return; // 如果不需要轮询,直接返回

  controller = new AbortController(); // 每次创建新的控制器
  try {
    const response = await fetch('/api/data', { signal: controller.signal });
    const data = await response.json();
    console.log('获取的数据:', data);
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('请求已取消');
    } else {
      console.error('获取失败', error);
    }
  }

  // 继续轮询
  setTimeout(fetchData, 5000);
}

// 启动轮询
fetchData();

// 需要停止轮询时调用
function stopPolling() {
  isPolling = false;
  controller.abort();
}
  • 优点
    • 可随时手动停止轮询,避免不必要的请求
    • 避免在组件卸载时继续请求

5. 基于 WebSocket 的轮询替代方案

如果轮询的目的是实时获取数据,WebSocket 可能是更好的选择:

javascript
const socket = new WebSocket('wss://example.com/socket');

socket.onopen = () => {
  console.log('WebSocket 连接已建立');
};

socket.onmessage = (event) => {
  console.log('收到数据:', event.data);
};

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

socket.onclose = () => {
  console.log('WebSocket 连接已关闭');
};
  • 优点
    • 实时性更高
    • 避免了不必要的轮询
  • 缺点
    • 服务器需要支持 WebSocket
    • 可能需要额外的连接管理(如断线重连)

6. 使用 requestAnimationFrame 轮询(适用于前端状态检测)

如果需要更高效地在浏览器刷新帧中执行轮询(如 UI 状态检查),可以使用 requestAnimationFrame

javascript
let polling = true;

function poll() {
  if (!polling) return;
  
  fetch('/api/data')
    .then(response => response.json())
    .then(data => console.log('获取的数据:', data))
    .catch(error => console.error('获取失败', error));

  requestAnimationFrame(poll);
}

poll();

// 停止轮询
function stopPolling() {
  polling = false;
}
  • 优点
    • 适用于前端状态检查,不占用 setTimeout
    • 结合浏览器的刷新帧,减少性能开销
  • 缺点
    • 适用于前端动画、UI 变化检测,而非网络请求

总结

方式适用场景优点缺点
setInterval简单的定期请求实现简单可能请求堆积,固定间隔
setTimeout需要避免请求堆积请求完成后再等待代码稍复杂
async/await 递归结构清晰易读性好仍需手动控制
AbortController需要手动停止轮询可以随时取消需要管理控制器
WebSocket实时数据更高效服务器需要支持
requestAnimationFrameUI 状态轮询利用刷新帧不适用于网络请求

推荐:

  • 短时间间隔(如 1~5 秒):使用 setTimeout 递归轮询,避免请求堆积
  • 可以停止的轮询:使用 AbortController
  • 实时数据:优先使用 WebSocket
  • 前端 UI 轮询:使用 requestAnimationFrame

长轮询与短轮询

轮询技术

轮询的概念

轮询是一种客户端与服务器之间实时通信的技术手段,它的基本原理是客户端定期发送请求来查询服务器是否有新数据或事件,并将响应返回给客户端。如果服务器有新的数据或事件,则将其返回给客户端;如果没有,则返回一个空响应。客户端收到响应后,可以处理数据或事件,并根据需要继续发送下一个请求。

轮询的实现原理

轮询的实现原理很简单,客户端期发送HTTP请求给服务器并等待响应。客户端可以使用定时器来定期发送请求,通常间隔时间设置为几秒钟到几分钟。服务器收到请求后检查是否有新数据或事件,并将其返回给客户端作为响应。客户端收到响应后,处理数据或事件,并随后发送下一个请求。

javascript
// 客户端代码
function pollServer() {
  fetch('/api/data')
    .then(response => response.json())
    .then(data => {
      // 处理服务器响应的数据
      console.log('Received data:', data      // 继续下一次轮询
      setTimeout(pollServer, 5000);
    });
}

// 开始轮询
pollServer();
javascript
// 服务器端代码 (使用 Express 框架)
const express = require('express');
const app = expressapp.get('/api/data', (req, res) => {
  // 假设需要返回的数据为 { "status": "ok", "data": ... }
  const responseData = {
    status: 'ok',
    data: ... // 根据实际需求提供数据
  };

  res.json(responseData);
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

轮询的优缺点

轮询的优点是简单易实现,适用于各种浏览器和服务器。然而,由于轮询的定期发送请求的特性,它会产生不必要的网络流量和延迟,并对服务器和客户端资源造成额外的负担。

轮询的使用场景

轮询适用于不需要实时性的应用场景,例如在线聊天室、实天气更新等。在这些场景下,短暂的延迟不会对用户体验产生重大影响。


长轮询技术

长轮询的概念

长轮询是一种改进的轮询技术,其主要目的是降低轮询过程中的资源消耗和延迟。长轮询的基本原理是客户端发送一个HTTP请求给服务器,并保持连接打开,直到服务器有新的数据或事件时才返回响应给客户端。在这期间,服务器会一直保持连接打开,直到超时或有新数据或事件。

长轮询的实现原理

长轮询的实现原理与轮询类似,只是客户端的请求会保持打开状态,直到服务器返回响应或超时。在服务器端,可以使用阻塞方式处理长轮询请求,即服务器线程会一直等待直到有新的数据或事件,然后返回响应给客户端。客户端收到响应后,可以处理数据或事件,并随后发送下一个长轮询请求。

javascript
// 客户端代码
function longPollServer() {
  fetch('/api/data')
    .then(response => {
      if (response.status === 204) {
        // 服务器返回204表示没有新数据或事件,继续进行长轮询
        longPollServer();
      } else if (response.status === 200) {
        // 服务器返回200表示有新数据或事件,处理数据并进行下一次长轮询
        response.json().then(data => {
          console.log('Received data:', data);

          // 继续进行长轮询
          longPollServer();
        });
      }
    });
}

//开始长轮询
longPollServer
javascript
// 服务器端代码 (使用 Express 框架)
const express = require('express');
const app = express();

let newData = null;

app.get('/api/data', (req, res) => {
  if (newData === null) {
    // 服务器新数据或事件,返回204
 res.sendStatus(204);
  } else    // 服务器有新数据或事件,返回200和数据
    res.json(newData);
    newData = null;
  }
});

// 更新数据或的路由,此处为示例代码,根据实际求进行修改
app.post('/api', (req, res) => {
 // 更新服务器的数据或事件
  newData = {
    ... // 根实际需求更新数据或事件
  };

  // 返回响应
 res.sendStatus(200);
});

app.listen(3000, () => {
  console.log('Server is running on port3000');
});

长轮询的优缺点

长轮询相较于轮询技术来说,减少了不必要的网络流量和请求次数,降低了服务器和客户端的资源消耗。但是相对于传统的轮询技术,长轮询的实现更加复杂,并且需要服务器支持长时间保持连接的能力。

长轮询的使用场景

长轮询适用于对实时性要求较高的应用场景,例如在线游戏、即时消息推送等。在这些场景下,降低延迟和减少不必要的资源消耗对于提供良好的用户体验非常重要。


轮询与长轮询的比较

轮询和长轮询都是实现实时通信的有效技术手段,但两者在资源消耗、延迟和实时性等方面有所不同。下表总结了两者的比较:

特性轮询长轮询
资源消耗
延迟较高较低
实时性较低较高
实现难度简单复杂

评论区

欢迎留言、补充或勘误。

xiaoba.blog