Django+JS聊天应用:房间超时未删除、时间停滞问题求助
问题描述
我用Django和JavaScript开发了一款多房间聊天应用,每个房间设置了超时自动删除机制。测试时发现一个问题:当一名用户在房间详情页,另一名用户在房间列表页时,房间到了超时时间后无法删除,剩余时间会停留在1秒。
现有代码
房间详情页 JavaScript
function updateRemainingTime() { // 向服务器发送AJAX请求 var xhr = new XMLHttpRequest(); xhr.open('GET', '/remaining-time/' + roomName + '/', true); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var remainingTime = JSON.parse(xhr.responseText); // 更新剩余时间 document.getElementById('remaining-time').textContent = remainingTime.minutes + ':' + remainingTime.seconds; if (remainingTime.minutes <= 0 && remainingTime.seconds <= 0) { deleteRoom(roomName); } } }; xhr.send(); } function deleteRoom(roomId) { var xhr = new XMLHttpRequest(); xhr.open('POST', '/delete-room/' + roomId + '/', true); xhr.setRequestHeader('X-CSRFToken', '{{ csrf_token }}'); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { window.location.href = '/home'; location.reload() } }; xhr.send(); } setInterval(updateRemainingTime, 1000);
房间列表页 JavaScript
function updateRemainingTime(roomId) { // 向服务器发送AJAX请求 var xhr = new XMLHttpRequest(); xhr.open('GET', '/remaining-time/' + roomId + '/', true); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var remainingTime = JSON.parse(xhr.responseText); // 更新剩余时间 var remainingTimeElement = document.getElementById('remaining-time-' + roomId); remainingTimeElement.textContent = remainingTime.minutes + ':' + remainingTime.seconds; if (remainingTime.minutes <= 0 && remainingTime.seconds <= 0 ) { // 剩余时间为0或负数时删除房间 deleteRoom(roomId); } } }; xhr.send(); } // 按固定间隔更新每个房间的时间 function deleteRoom(roomId) { // 向服务器发送AJAX请求 var xhr = new XMLHttpRequest(); xhr.open('POST', '/delete-room/' + roomId + '/', true); xhr.setRequestHeader('X-CSRFToken', '{{ csrf_token }}'); // 按需添加CSRF令牌 xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { location.reload() } }; xhr.send(); } room_ids.forEach(function(roomId) { setInterval(function() { updateRemainingTime(roomId); }, 1000); // 每秒更新一次 });
后端API (api.py)
def remaining_time_api(request, room_id): room = Room.objects.get(id=room_id) remaining_time = room.remaining_time() seconds = remaining_time.seconds % 60 minutes = (remaining_time.seconds // 60) % 60 return JsonResponse({'minutes': minutes, 'seconds': seconds}) def delete_room(request, room_id): room = get_object_or_404(Room, id=room_id) room.delete() return redirect('/home')
问题根源
- 并发删除冲突:两个页面同时发起删除请求时,第一个请求删除房间后,第二个请求会触发
Room.DoesNotExist异常,但后端未处理该异常,返回500错误;前端只处理200状态,忽略错误,继续请求剩余时间,导致时间停留在最后一次成功获取的1秒。 - 前端错误处理缺失:AJAX请求仅处理成功状态,未处理404/500等错误,房间删除后仍持续发起无效请求。
- 删除接口返回不合理:
delete_room返回重定向,AJAX请求无法自动处理该响应,且前端存在冗余的页面刷新逻辑。
修复方案
1. 后端API优化
处理房间不存在的异常
from django.http import Http404 def remaining_time_api(request, room_id): try: room = Room.objects.get(id=room_id) except Room.DoesNotExist: raise Http404("Room does not exist") remaining_time = room.remaining_time() seconds = remaining_time.seconds % 60 minutes = (remaining_time.seconds // 60) % 60 return JsonResponse({'minutes': minutes, 'seconds': seconds})
修改删除接口返回JSON
def delete_room(request, room_id): room = get_object_or_404(Room, id=room_id) room.delete() return JsonResponse({'success': True}, status=200)
2. 房间详情页JS修复
增加错误处理,清除无效定时器,优化跳转逻辑:
let timeInterval; // 保存定时器ID function updateRemainingTime() { var xhr = new XMLHttpRequest(); xhr.open('GET', '/remaining-time/' + roomName + '/', true); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { var remainingTime = JSON.parse(xhr.responseText); // 格式化秒数为两位数 const formattedSeconds = remainingTime.seconds.toString().padStart(2, '0'); document.getElementById('remaining-time').textContent = `${remainingTime.minutes}:${formattedSeconds}`; if (remainingTime.minutes <= 0 && remainingTime.seconds <= 0) { deleteRoom(roomName); } } else if (xhr.status === 404) { // 房间已被删除,清除定时器并跳转 clearInterval(timeInterval); window.location.href = '/home'; } } }; xhr.send(); } function deleteRoom(roomId) { var xhr = new XMLHttpRequest(); xhr.open('POST', '/delete-room/' + roomId + '/', true); xhr.setRequestHeader('X-CSRFToken', '{{ csrf_token }}'); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200 || xhr.status === 404) { clearInterval(timeInterval); window.location.href = '/home'; } } }; xhr.send(); } timeInterval = setInterval(updateRemainingTime, 1000);
3. 房间列表页JS修复
增加错误处理,移除无效DOM元素,避免全局刷新:
// 存储每个房间的定时器ID const roomIntervals = {}; function updateRemainingTime(roomId) { var xhr = new XMLHttpRequest(); xhr.open('GET', '/remaining-time/' + roomId + '/', true); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { var remainingTime = JSON.parse(xhr.responseText); const formattedSeconds = remainingTime.seconds.toString().padStart(2, '0'); var remainingTimeElement = document.getElementById('remaining-time-' + roomId); if (remainingTimeElement) { remainingTimeElement.textContent = `${remainingTime.minutes}:${formattedSeconds}`; } if (remainingTime.minutes <= 0 && remainingTime.seconds <= 0) { deleteRoom(roomId); } } else if (xhr.status === 404) { // 清除定时器并移除房间元素 clearInterval(roomIntervals[roomId]); delete roomIntervals[roomId]; const roomElement = document.getElementById('room-' + roomId); if (roomElement) roomElement.remove(); } } }; xhr.send(); } function deleteRoom(roomId) { var xhr = new XMLHttpRequest(); xhr.open('POST', '/delete-room/' + roomId + '/', true); xhr.setRequestHeader('X-CSRFToken', '{{ csrf_token }}'); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200 || xhr.status === 404) { clearInterval(roomIntervals[roomId]); delete roomIntervals[roomId]; const roomElement = document.getElementById('room-' + roomId); if (roomElement) roomElement.remove(); } } }; xhr.send(); } // 初始化定时器 room_ids.forEach(function(roomId) { roomIntervals[roomId] = setInterval(() => updateRemainingTime(roomId), 1000); });
额外优化建议
- 用WebSocket替代轮询:减少服务器压力,实现房间状态的实时推送。
- 后端统一时间格式化:直接返回
00:01格式的字符串,简化前端逻辑。 - 增加房间超时的后端定时任务:避免依赖前端触发删除,提升可靠性。
内容的提问来源于stack exchange,提问作者Mert Altintas
相关产品推荐
相关产品推荐

