7

<script>页面加载后,我正在向 head 元素动态添加一些标签。我知道脚本是异步加载的,但是我可以期望它们按添加顺序进行解析吗?

我在 Firefox 中看到了预期的行为,但在 Safari 或 Chrome 中没有。查看 Chrome 开发人员工具和 Firebug 中的文档,均显示以下内容 -

<html>
  <head>
    ...
    <script type="text/javascript" src="A.js"></script>
    <script type="text/javascript" src="B.js"></script>
  </head>
  ...
</html>

但是查看资源加载视图,chrome 似乎会解析服务器首先返回的内容,而 firebug 总是按照添加脚本标签的顺序加载它们,即使 B 首先从服务器返回。

我应该期望 Chrome/Safari 以指定的顺序解析文件吗?在 OS X 10.6.3 上使用 Chrome 5.0.375.29 测试版

编辑(10/5/10):当我说解析时,我的意思是执行 - 可以看到积极解析的许多好处 - thx rikh

编辑(10 年 11 月 5 日):好的,所以我按照下面 juandopazo 的思路进行了测试。但是我添加了一些东西,包括

  1. 使用 javascript 直接将脚本元素添加到头部。(测试 A -> D)
  2. 使用 jquery 的 append() 方法将脚本元素添加到头部。(测试 E -> H)
  3. 使用 jquery 的 getScript() 方法“加载”脚本。(测试 I -> L)

我还尝试了脚本标签上“异步”和“延迟”属性的所有组合。

您可以在此处访问测试 - http://dyn-script-load.appspot.com/,并查看源代码以了解它是如何工作的。加载的脚本只需调用 update() 函数。

首先要注意的是,只有上面的第 1 和第 3 方法并行运行 - 第 2 方法按顺序执行请求。你可以在这里看到这个图表 -

图 1 - 请求生命周期图 请求生命
周期图 http://dyn-script-load.appspot.com/images/dynScriptGraph.png

有趣的是,jquery append() 方法也阻塞了 getScript() 调用——你可以看到它们都不会执行,直到所有 append() 调用都完成,然后它们都并行运行。最后要注意的是,jQuery append() 方法在执行后显然会从文档头部删除脚本标签。只有第一种方法将脚本标签留在文档中。

铬结果

结果是 Chrome 总是执行第一个返回的脚本,不管测试如何。这意味着所有测试“失败”,除了 jQuery append() 方法。

图 2 - Chrome 5.0.375.29 beta 结果
Chrome 结果 http://dyn-script-load.appspot.com/images/chromeDynScript.png

火狐结果

然而,在 firefox 上,如果使用第一种方法,并且 async 为 false(即未设置),那么脚本将可靠地按顺序执行。

图 3 - FF 3.6.3 结果
FF 结果 http://dyn-script-load.appspot.com/images/ffDynScript.png

请注意,Safari 似乎以与 Chrome 相同的方式给出不同的结果,这是有道理的。

此外,我在慢速脚本上只有 500 毫秒的延迟,只是为了保持开始->结束时间。您可能需要刷新几次才能看到 Chrome 和 Safari 在所有方面都失败了。

在我看来,如果没有这样做的方法,我们就没有利用并行检索数据的能力,也没有理由不应该这样做(如 Firefox 所示)。

4

6 回答 6

3

很抱歉回答我自己的问题,但已经有一段时间了,我们确实想出了一个解决方案。我们想出的是将 javascript 作为包含在 json 对象中的文本同时加载,然后在它们全部加载后使用 eval() 以正确的顺序执行它们。并发加载加上有序执行。根据您的用例,您可能不需要 json。粗略地说,这里有一些代码显示了我们所做的 -

// 'requests' is an array of url's to javascript resources
var loadCounter = requests.length;
var results = {};

for(var i = 0; i < requests.length; i++) {
   $.getJSON(requests[i], function(result) {
      results[result.id] = result;
      ...
      if(--loadCounter == 0) finish();
   });
}

function finish() {
  // This is not ordered - modify the algorithm to reflect the order you want
  for(var resultId in results) eval(results[resultId].jsString);
}
于 2010-07-30T14:45:46.343 回答
0

下载顺序和执行顺序不是一回事。在您的页面中,即使先下载了 B.js,浏览器的引擎也会等待 A.js 继续处理该页面。

脚本肯定是经过处理的,不仅按照它们在文档中出现的顺序,而且在它们出现的位置。

想象一下,如果不是这样的话,如果你的使用 jQuery 的小脚本在 jQuery 库之前被下载和处理,将会出现很多错误。

此外,当您在 js 文件中执行“document.write”时,它会出现在声明脚本的位置。您也不能访问出现在脚本声明之后的 DOM 对象。

这就是为什么建议将脚本放在页面最底部的原因,以防止它们过早执行并减少页面的“感知加载时间”,因为一旦处理脚本,浏览器的渲染引擎就会停止。

麦克风

编辑:如果它们是用javascript动态添加的,我认为它们是按照它们及时添加的顺序处理的。

于 2010-05-10T16:15:53.340 回答
0

据我了解,它们应该按照它们在文档中出现的顺序执行。一些浏览器可能能够执行一些无序的解析,但它们仍然必须以正确的顺序执行。

于 2010-05-10T16:07:11.010 回答
0

不,您不能期望所有浏览器都会推迟两个脚本的执行,直到它们都被加载(**特别是当您动态添加它们时)。

如果您只想在加载后执行代码,B.js那么A.js您最好的办法是添加一个onload回调来A.js设置一个变量,然后再添加一个回调来B.js检查该变量是否已设置,然后它执行必要的函数,B.js如果它有(如果A.js还没有加载,它会启动一个计时器,定期检查直到它加载)。

于 2010-05-10T16:10:44.427 回答
0

我在研究一个像 YUI 3 一样动态加载模块的小库时对此进行了调查。我在这里创建了一个小测试,它加载了两个仅将内容插入到 div 中的脚本。一个是普通的JS文件,另一个是等待3秒执行的PHP文件。

http://www.juandopazo.com.ar/tests/asyn-script-test.html

如您所见,脚本在完成加载后执行,而不是按照您将它们附加到 DOM 的顺序在每个浏览器中执行。

于 2010-05-10T16:49:35.280 回答
0

您可以从a.js加载b.js以确保 100% 确定......虽然我自己想要这个问题的明确答案,尤其是在同步 ajax 加载脚本的情况下。

于 2010-05-10T16:44:54.803 回答