如何用Ajax记录用户离开、返回考试页面的状态并减少冗余请求
在线考试页面状态检测优化方案
这套方案彻底解决高频轮询导致的冗余请求、冗余数据库写入问题,同时保证状态切换的捕捉精度:
核心实现逻辑
通过本地状态缓存对比的逻辑,从根源上避免重复上报:
- 用一个全局变量存储「上一次已经成功上报过的用户状态」,页面加载完成时直接取当前焦点状态作为初始缓存值,初始值不触发任何上报请求
- 每次检测到当前用户的焦点状态时,先和本地缓存的上一次状态做值对比:
- 两个状态完全一致:说明用户没有发生离开/返回的动作,直接跳过流程,不发送任何请求
- 两个状态存在差异:说明刚触发了状态切换(离开页面/返回页面),这时候才向后端发送对应的状态上报请求,请求成功后再更新本地缓存的状态值
优化后的前端实现代码
// 初始化缓存的上次上报状态,取页面加载时的初始焦点状态,不触发初始上报 let lastReportedStatus = document.hasFocus() ? 'Present' : 'Absent'; /** * 状态上报方法:仅当状态和上次上报值不同时才触发请求 */ function reportExamStatus(currentStatus) { // 状态无变化直接终止,不发请求 if (currentStatus === lastReportedStatus) return; $.ajax({ type: "POST", url: 'submission.php', data: { status: currentStatus, clientTimestamp: Date.now() // 附带前端时间戳,减少后端时钟偏差影响 }, success: () => { // 上报成功后更新本地缓存的状态值 lastReportedStatus = currentStatus; }, error: () => { // 请求失败时不更新缓存,下次同状态检测时会自动重试 console.warn('状态上报失败,将在下一次状态检测时重试'); } }); } // 利用浏览器原生事件精准捕捉状态切换,无需高频轮询 // 覆盖场景:切换浏览器标签页、最小化考试窗口 document.addEventListener('visibilitychange', () => { const currentStatus = document.visibilityState === 'visible' ? 'Present' : 'Absent'; reportExamStatus(currentStatus); }); // 覆盖场景:点击考试窗口外的其他程序窗口,导致页面失焦 window.addEventListener('focus', () => reportExamStatus('Present')); window.addEventListener('blur', () => reportExamStatus('Absent')); // 可选兜底机制:30秒一次的低频轮询核对状态,防止极端场景下原生事件漏触发,替代原来2秒高频轮询的冗余模式 setInterval(() => { const currentStatus = document.hasFocus() ? 'Present' : 'Absent'; reportExamStatus(currentStatus); }, 30000);
后端配套优化建议
为进一步避免数据库冗余,PHP端接收请求时增加一层状态校验:
收到前端上报的状态后,先查询当前考试用户最新的一条状态记录
- 如果最新记录的状态和本次上报的状态完全一致,直接返回成功,不执行数据库写入
- 如果状态存在差异,才新增一条记录,存储对应考试ID、用户ID、上报状态、时间戳、用户IP
实际运行效果
整套逻辑跑通后:
- 用户全程停留在考试页面时,除了30秒一次的兜底核对,不会产生任何额外的状态上报请求,更不会触发无意义的数据库写入
- 所有入库的记录都是真正的状态切换节点,后续计算离开时长时,直接取相邻的
Absent和Present状态的时间戳做差即可,不需要额外清洗冗余数据
内容的提问来源于stack exchange,提问作者Benjamin
相关产品推荐
相关产品推荐

