【问题标题】:How to check that XMLHttpRequest connection was established before data arrive如何在数据到达之前检查 XMLHttpRequest 连接是否已建立
【发布时间】:2019-10-10 13:49:00
【问题描述】:

我正在使用EventSource 实现一些服务器端事件,我需要知道何时建立EventSource 连接并且服务器以初始200 OK 响应,然后我可以开始执行一些最终导致消息的其他请求从服务器通过 EventSource 发送。

我实际上使用了这个 polyfill https://github.com/AlexGalays/EventSource,它在内部使用了 XMLHttpRequest。

问题:当服务器发送 200 OK + headers 时,onreadystatechange 没有被触发(xhr.readyState 仍然是1)。这是与任何 XHR 相关的普遍问题,而不仅仅是 EventSource。

PHP 服务器示例:

<?php
sleep(5); // our actual implementation does some non-trivial startup here
// send 200 OK + headers, but no data
flush();

sleep(5);
echo "bye";

示例 - 客户:

<script>
    xhr = new XMLHttpRequest();
    xhr.onreadystatechange = () => console.log(`readystate = ${xhr.readyState}`);
    xhr.open('GET', 'http://localhost/longpoll.php');
    xhr.send();
</script>

预期行为:

  • readystate = 1(打开)
  • 5 秒延迟
  • readystate = 2(收到标头)
  • 5 秒延迟
  • 就绪状态 = 3(加载中)
  • 就绪状态 = 4(完成)

实际行为:

  • readystate = 1(打开)
  • 10 秒延迟
  • readystate = 2(收到标头)
  • 就绪状态 = 3(加载中)
  • 就绪状态 = 4(完成)

在最新的 Chrome (77) 和 Firefox (69) 中测试,两者的行为相同。

当我在 Chrome 开发工具 Network 选项卡中观察连接时,我确实在前 5 秒延迟后看到了响应标头和状态代码(请参阅 https://youtu.be/sIgQnbfwxjM)。这意味着浏览器在前 5 秒后真正接收到标头并更新连接状态,但 JavaScript 未被确认。

这可以通过某种方式解决吗?还是某些安全限制阻止我在此阶段获取更新的连接状态?

【问题讨论】:

    标签: javascript xmlhttprequest


    【解决方案1】:

    默认情况下,Chrome 和其他浏览器会缓冲 XMLHttpRequest 的整个响应(标头和正文)。因此,没有简单的选项可以先检查标头,然后使用 XMLHttpRequest 获取正文。唯一的解决方案是将响应作为流/块处理,而不是单个响应。在 Firefox 中有 "moz-chunked-arraybuffer" 对应于 xhr.responseType,但它一直是 removed。 Google Chrome 在某个时候实现了 xhr.responseType "stream" (as experimental),但它一直是 dropped in favor of whatwg Streams。今天,大多数现代浏览器中最简单的fetch API 使我们可以获取没有正文的响应标头(如果服务器已经发送)。在 Chrome 77 中测试:

    let response = await fetch('http://localhost:9615/');
    
    if (response.ok) {
      console.log('status code:', response.status);
      let text = await response.text();
      console.log('text', text);
    } else {
      console.error('HTTP-Error:', response.status);
    }
    

    你也可以使用直接提到的whatwg Streams (ReadableStream),比如这里:

    fetch("http://localhost:9615/").then((response) => {
      const reader = response.body.getReader();
      const stream = new ReadableStream({
        start(controller) {
          console.log('status code:', response.status);
          function push() {
            reader.read().then(({ done, value }) => {
              console.log('read', value);
              if (done) {
                console.log('done');
                controller.close();
                return;
              }
    
              controller.enqueue(value);
              push();
            });
          };
    
          push();
        }
      });
    
      return new Response(stream, { headers: { "Content-Type": "text/html" } });
    });
    

    作为测试服务器,我使用了以下代码(NodeJS):

    const http = require('http');
    
    http.createServer(function (req, res) {
        setTimeout(() => {
          res.writeHead(200, {'Content-Type': 'text/plain'});
          res.flushHeaders();
          setTimeout(() => {
            res.write("some content");
            res.end();
          }, 3000);
        }, 3000);
    }).listen(9615);
    

    在这两种解决方案(Fetch API 和 ReadableStream)中,您将在前 3000 毫秒后收到 status code: 200,在接下来的 3000 毫秒后收到其余响应。

    不幸的是,Streams 尚未被浏览器广泛支持,但 here 是一个 polyfill。另见this

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-05
      • 2020-03-31
      • 1970-01-01
      • 2015-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多