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

Jam*_*ong 8 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>
Run Code Online (Sandbox Code Playgroud)

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

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

什么实际发生在Firefox是所有5个.js文件请求并行,他们无法连接,那么最后4得到的序列重新请求.像这样:

在此输入图像描述

为什么?

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

特定示例不会在Chrome中重现相同的行为,但其他类似的示例是我最初如何依赖此行为.

编辑:这是我测试这种情况的方式,当它也是404时.

$ cd /tmp
$ mkdir empty
$ cd empty
$ python -m SimpleHTTPServer 1234
Run Code Online (Sandbox Code Playgroud)

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

![在此处输入图像说明

服务器实际上也看到了所有这些请求(前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 -
Run Code Online (Sandbox Code Playgroud)

Ale*_*ara 10

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

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

网络面板

正如我们所看到的,所有5个脚本都是现代浏览器并行请求的,以减少加载时间.

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

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

那么为什么这个问题呢?

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

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

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

网络面板2

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

有趣的是,我在规范中找不到任何关于此的内容,但是"生活标准"中的这一部分表明这种行为实际上可能违反了规范.

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

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

注意:

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