Chrome在Connection关闭时无法处理XMLHttpRequest响应问题咨询
Chrome在Keep-Alive连接关闭时返回XHR状态码0的原因及解决方法
这个问题我之前也碰到过,Chrome对HTTP持久连接(Keep-Alive)的关闭处理逻辑确实和其他浏览器有差异,尤其是在复用XMLHttpRequest实例的场景下。
核心原因分析
- 复用XHR实例的兼容性问题:你的代码里一直在复用同一个
this._request实例,虽然XHR规范允许复用实例(重新调用open和send),但Chrome的网络栈在处理服务器发送的Connection: close头部时,会立即终止TCP连接,而复用的XHR实例无法正确处理这种连接主动关闭的场景,导致请求直接失败,返回状态码0且无响应文本。其他浏览器(Firefox/Safari/Opera)在这种情况下会更宽松地处理复用实例,允许完成最后一次响应的解析。 - Chrome对连接关闭的严格校验:当服务器在第10次响应中发送
Connection: close时,Chrome会立即标记该连接为不可用,即使当前请求的响应还在传输过程中,也会终止XHR的处理流程,直接触发error事件或者将readyState设为DONE但状态码为0。而其他浏览器会等待当前响应完全接收后再关闭连接,所以能正常获取响应内容。
解决方案
针对这个问题,有几个可行的修复方向:
1. 每次请求创建新的XHR实例
不要再复用同一个this._request,每次调用send方法时都创建新的XMLHttpRequest对象。这样可以避免旧实例的连接状态影响新请求,Chrome就能正确处理每次请求的连接生命周期:
function send(method, data) { try { const request = new XMLHttpRequest(); request.onreadystatechange = (event) => { switch (request.readyState) { case XMLHttpRequest.OPENED: { // Do something } break; case XMLHttpRequest.LOADING: { // Do something } break; case XMLHttpRequest.DONE: { // 处理响应,包括状态码0的情况 if (request.status === 0) { // 可以在这里重试请求或者提示用户 } else { // 正常处理响应 } } break; } }; request.addEventListener('error', (event) => { // Do something }); request.addEventListener('abort', (event) => { // Do something }); request.open(String(method), this.path); request.timeout = 200; // 设置请求头部等初始化操作 request.send(data); // 如果需要保存实例,可以把request赋值给this._request,但每次请求都替换 this._request = request; } catch (error) {} }
2. 处理状态码0的重试逻辑
如果一定要复用实例,可以在DONE状态中检测到status === 0时,重新创建实例并发送当前请求。不过这种方式不如每次创建新实例可靠,因为你需要处理连接关闭后的实例状态重置问题。
3. 调整超时时间(可选)
你的代码设置了timeout = 200,这个时间非常短,可能会在服务器关闭连接的过程中触发超时,导致状态码0。可以适当延长超时时间,比如设置为1000或者根据实际场景调整,排除超时因素的影响。
额外说明
Chrome的这种行为其实是符合HTTP规范的,因为Connection: close头部明确要求服务器在响应后关闭连接,Chrome只是更严格地执行了这个要求。而其他浏览器的处理属于“容错性”更强的实现,但从规范角度来说,Chrome的处理并没有问题,只是兼容性上需要我们调整代码来适配。
内容的提问来源于stack exchange,提问作者wemb




