为什么浏览器会在非200响应上重新请求脚本?

时间:2016-11-02 02:05:53

标签: javascript browser

将以下HTML保存为本地文件。像/tmp/foo.html这样的东西,然后在Firefox中打开(我在49.0.2上)

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="http://localhost:1234/a.js"></script>
<script src="http://localhost:1234/b.js"></script>
<script src="http://localhost:1234/c.js"></script>
<script src="http://localhost:1234/d.js"></script>
<script src="http://localhost:1234/e.js"></script>
</body>
</html>

我没有在端口1234上运行服务器,因此请求甚至无法成功连接。

我在这里期望的行为是所有失败的请求,并且完成它。

在Firefox中发生实际的是所有5个.js文件并行请求,它们无法连接,然后最后4个连续重新请求。像这样:

enter image description here

为什么?

如果我在1234上启动始终为404s的服务器,则行为是相同的。

这个特定的示例在Chrome中没有重现相同的行为,但其他类似的例子就是我最初对此行为的看法。

编辑:以下是我测试过这种情况的方法,也就是404。

$ cd /tmp
$ mkdir empty
$ cd empty
$ python -m SimpleHTTPServer 1234

然后重新加载Firefox。它显示了这个:

![enter image description here

服务器实际上也看到了所有这些请求(前5个不按顺序到达,因为它们并行请求,但最后4个总是b,c,d,e,因为它们会被重新请求串行)。

127.0.0.1 - - [02/Nov/2016 13:25:40] code 404, message File not found
127.0.0.1 - - [02/Nov/2016 13:25:40] "GET /d.js HTTP/1.1" 404 -
127.0.0.1 - - [02/Nov/2016 13:25:40] code 404, message File not found
127.0.0.1 - - [02/Nov/2016 13:25:40] "GET /c.js HTTP/1.1" 404 -
127.0.0.1 - - [02/Nov/2016 13:25:40] code 404, message File not found
127.0.0.1 - - [02/Nov/2016 13:25:40] "GET /b.js HTTP/1.1" 404 -
127.0.0.1 - - [02/Nov/2016 13:25:40] code 404, message File not found
127.0.0.1 - - [02/Nov/2016 13:25:40] "GET /a.js HTTP/1.1" 404 -
127.0.0.1 - - [02/Nov/2016 13:25:40] code 404, message File not found
127.0.0.1 - - [02/Nov/2016 13:25:40] "GET /e.js HTTP/1.1" 404 -
127.0.0.1 - - [02/Nov/2016 13:25:40] code 404, message File not found
127.0.0.1 - - [02/Nov/2016 13:25:40] "GET /b.js HTTP/1.1" 404 -
127.0.0.1 - - [02/Nov/2016 13:25:40] code 404, message File not found
127.0.0.1 - - [02/Nov/2016 13:25:40] "GET /c.js HTTP/1.1" 404 -
127.0.0.1 - - [02/Nov/2016 13:25:40] code 404, message File not found
127.0.0.1 - - [02/Nov/2016 13:25:40] "GET /d.js HTTP/1.1" 404 -
127.0.0.1 - - [02/Nov/2016 13:25:40] code 404, message File not found
127.0.0.1 - - [02/Nov/2016 13:25:40] "GET /e.js HTTP/1.1" 404 -

1 个答案:

答案 0 :(得分:10)

这与并行资源加载可能出现的边缘情况有关,其中JavaScript预计会阻止其他资源加载。

当您在错误响应中添加延迟时,此行为开始变得更加清晰。以下是Firefox网络面板的屏幕截图,每个请求都添加了1秒的延迟。

network panel

正如我们所看到的,并行请求了所有5个脚本as modern browser do,以减少加载时间。

但是,除了第一个,返回404的脚本是重新请求的,不是并行而是串行。这几乎可以肯定地保持与传统浏览器行为的一些边缘情况的向后兼容性。

历史上,浏览器会一次加载并执行一个脚本。现代浏览器将并行加载它们,同时仍保持执行顺序。

那为什么会这么重要?

想象一下,如果第一个脚本请求更改了应用程序状态,可能会设置cookie或其他内容来验证其他请求。使用新的并行加载,在更改此状态之前将请求这些脚本,并且假设Web应用程序设计得足够好,则会抛出错误。

因此,确保其他资源没有错误的唯一方法是因为脚本在被请求之前没有机会更改状态是重新请求资源。

事实上,这种重新请求行为并不仅限于脚本,也可以看到在并行加载的脚本标记之后影响错误的图像。

network panel 2

可能,因为这些图像可能无法加载,因为先前的脚本没有先执行,所以它们都是并行重新请求的。

有趣的是,我无法在规范中直接找到任何相关内容,但The Living Standard中的这一部分表明此行为实际上可能违反规范。

  

对于经典脚本,如果存在async属性,则经典脚本将被并行提取以进行解析并在可用时立即进行评估(可能在解析完成之前)。如果async属性不存在但存在defer属性,则将并行获取经典脚本,并在页面完成解析时进行评估。如果两个属性都不存在,则立即获取并评估脚本,阻止解析直到这些都完成。

如果解析实际上被阻止了,那么看起来似乎不应该读取以下脚本标记和图像以便能够加载。我怀疑浏览器通过在执行之前不在DOM中提供以下标记来协调此问题。

注意:

在这些情况下您将看到的确切行为可能会有所不同。实际上只会重新加载与脚本并行实际请求的那些资源。如果图像后来出现错误,但在加载脚本时没有请求,则无需重新请求它。此外,如果潜在状态更改脚本出现 not 错误,Chrome似乎只会触发此行为,但Firefox会触发此行为,即使它发生错误。