You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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')  

问题根源

  1. 并发删除冲突:两个页面同时发起删除请求时,第一个请求删除房间后,第二个请求会触发Room.DoesNotExist异常,但后端未处理该异常,返回500错误;前端只处理200状态,忽略错误,继续请求剩余时间,导致时间停留在最后一次成功获取的1秒。
  2. 前端错误处理缺失:AJAX请求仅处理成功状态,未处理404/500等错误,房间删除后仍持续发起无效请求。
  3. 删除接口返回不合理: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 16:52:43