Bootstrap

ajax用https请求不了_Chrome滚动事件概率性Block Ajax请求

最近发现一件有意思的事情,在Chrome 61 62 64 68版本上(也许不止这些版本),滚动事件会概率性Block住Ajax请求或者其它的异步事件,具体表现为,只要用户使用鼠标滚轮滚动页面,请求就一直Pending, Promise、setTimeout 这些异步行为也不会往下走。如果直接拖动滚动条,则不会出现这一问题。

这个问题多出现在滚动到底/顶触发请求的场景,比如聊天列表滚动到顶拉取IM消息,网页滚动到底拉取新的博客这种。

可以自己测试一下玩玩看,HTML代码如下(知乎竟然不支持上传html文件...)。

这个问题不是必现的,感觉上似乎得滚动得很快才能复现,本组测试妹子使用这个html可以多次复现,可以清楚地看到图片请求被Pending在那里,即便她停止滚动,请求依旧Pending,而通过滚动条触发的请求则不会被Pending。(那一刻我震惊了...)

<!--
  Reproduction of https://bugs.chromium.org/p/chromium/issues/detail?id=661155
-->

<html>
  <head>
    <title>Scroll network throttling</title>

    <style>
      .fixed {
        position: fixed;
      }

      img {
        width: 120px;
        height: 120px;
        background: #777;
      }

      body {
        background: repeating-linear-gradient(to bottom, #ddd, #ddd 40px, #fff 40px, #fff 80px);
      }
    </style>
  </head>

  <body>
    <p>
      Scroll the page and don't stop (easiest on devices with inertia scrolling or a free-scrolling wheel). After 1000px of scrolling the image src will be set.
    </p>

    <div class="fixed">
      <img id="img" />
    </div>
    <div style="height: 20000%"></div>

    <script>
      // Bug only occurs if there is a touch listener attached to any element (even if marked as passive)
      document.body.addEventListener('touchstart', () => {});

      const initialScrollOffset = window.pageYOffset;
      const img = document.getElementById('img');

      const setSrc = () => {
        img.src = 'http://placeimg.com/200/200/any';
      }

      const scrollCallback = (e) => {
        if (window.pageYOffset - initialScrollOffset < 1000) {
          return;
        }

        document.removeEventListener('scroll', scrollCallback);
        setSrc();
      }

      document.addEventListener('scroll', scrollCallback)
    </script>
  </body>
</html>

所以,奇怪的知识增加了!开心!

——————————————————————————————————————

分割线后是详细的调查过程,主要研究目标是Content Download耗时过长问题。

事情的起因是这样的:

有用户报障说卡顿,远程看了一下发现请求的Timing里Content Download耗时特别久,虽然官方文档说Content Download只是Chrome从收到响应的第一个比特开始,到接收完所有数据的耗时,但是由于当时网络延迟很低,只有6ms,也没有丢包,所以我觉得3KB的东西不太可能传700ms,于是开始研究Content Download耗时这个问题。

d2e389bd8fc27813c65bbe0a144ac968.png

有博客[1]的问题描述跟我们比较相似,大致意思是Chrome浏览器对于mousewheel实现的不标准,即便使用scroll事件也会相互影响,解决方案是干掉window下mousewheel事件的默认行为。但是这个结论怎么看怎么诡异,同时我这边也不能复现问题,所以没有办法验证。

于是继续查,在这篇博客[2]里,有对应的gif录像,大致意思是用鼠标滚轮触发请求之后,在Chrome里会一直转圈,在其它浏览器里都正常,同时如果用鼠标拖动滚动条也是正常的,所以怀疑Chrome有bug,但是他试验出来网络请求就是一直Pending,并不会触发Content Download时间过长。

另外,在某个提问[3]中,也叙述了Chrome中使用 鼠标的滚动事件block住异步行为的问题。

同时,在Chrome的某个issue页[4]中找到了相关的描述和复现方式,请测试妹纸复现成功,验证在我们QT 5.12编译出来的 Chrome 61版本的应用中确实可以复现这个问题。

所以我想了一下,第一篇博客那个诡异的结论可能是在服务端返回第一个比特之后,再触发的异步行为被block,不过,我实在是复现不了。我能够确认的是,Chrome真的有滚动block异步行为的问题,不过可能触发条件太tricky了,所以直到现在,这个问题还是没有修复的记录[5]。

dd128eb9f8946234614c012e971ca787.png

相关链接:

[1] 关于ajax的content-download时间过慢问题的解决方案与思考

[2] https://github.com/TryGhost/Ghost/issues/7934

[3] https://stackoverflow.com/questions/35024301/xhr-settimeout-promise-not-finishing-until-scrolling-stops-in-chrome

[4] https://bugs.chromium.org/p/chromium/issues/detail?id=661155

[5] https://bugs.chromium.org/p/chromium/issues/detail?id=874836

;