Javascript 加载和执行脚本的顺序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8996852/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
load and execute order of scripts
提问by Bergi
There are so many different ways to include JavaScript in a html page. I know about the following options:
在 html 页面中包含 JavaScript 的方法有很多种。我知道以下选项:
- inline code or loaded from external URI
- included in <head> or <body> tag [1,2]
- having none,
defer
orasync
attribute (only external scripts) - included in static source or added dynamically by other scripts (at different parse states, with different methods)
- 内联代码或从外部 URI 加载
- 包含在 <head> 或 <body> 标签中 [ 1, 2]
- 没有,
defer
或async
属性(只有外部脚本) - 包含在静态源中或由其他脚本动态添加(在不同的解析状态下,使用不同的方法)
Not counting browserscripts from the harddisk, javascript:URIs and onEvent
-attributes [3], there are already 16 alternatives to get JS executed and I'm sure I forgot something.
不算来自硬盘的浏览器脚本、javascript:URIs 和onEvent
-attributes [ 3],已经有 16 种替代方法可以让 JS 执行,而且我确定我忘记了一些东西。
I'm not so concerned with fast (parallel) loading, I'm more curious about the execution order (which may depend on loading order and document order). Is there a good(cross-browser) reference that covers really all cases?E.g. http://www.websiteoptimization.com/speed/tweak/defer/only deals with 6 of them, and tests mostly old browsers.
我不太关心快速(并行)加载,我对执行顺序更好奇(这可能取决于加载顺序和文档顺序)。是否有涵盖所有情况的良好(跨浏览器)参考?例如http://www.websiteoptimization.com/speed/tweak/defer/只处理其中的 6 个,并且主要测试旧浏览器。
As I fear there's not, here is my specific question: I've got some (external) head scripts for initialisation and script loading. Then I've got two static, inline scripts in the end of the body. The first one lets the script loader dynamically append another script element (referencing external js) to the body. The second of the static, inline scripts wants to use js from the added, external script. Can it rely on the other having been executed (and why :-)?
我担心没有,这是我的具体问题:我有一些(外部)头脚本用于初始化和脚本加载。然后我在正文的末尾有两个静态的内联脚本。第一个让脚本加载器动态地将另一个脚本元素(引用外部 js)附加到正文中。第二个静态内联脚本想要使用添加的外部脚本中的 js。它可以依赖另一个已被执行(以及为什么:-)?
回答by jfriend00
If you aren't dynamically loading scripts or marking them as defer
or async
, then scripts are loaded in the order encountered in the page. It doesn't matter whether it's an external script or an inline script - they are executed in the order they are encountered in the page. Inline scripts that come after external scripts are held until all external scripts that came before them have loaded and run.
如果您没有动态加载脚本或将它们标记为defer
或async
,则脚本将按照页面中遇到的顺序加载。无论是外部脚本还是内联脚本都无关紧要 - 它们按照在页面中遇到的顺序执行。在外部脚本之后的内联脚本被保留,直到它们之前的所有外部脚本都加载并运行。
Async scripts (regardless of how they are specified as async) load and run in an unpredictable order. The browser loads them in parallel and it is free to run them in whatever order it wants.
异步脚本(无论它们如何指定为异步)以不可预测的顺序加载和运行。浏览器并行加载它们,并且可以按照它想要的任何顺序自由运行它们。
There is no predictable order among multiple async things. If one needed a predictable order, then it would have to be coded in by registering for load notifications from the async scripts and manually sequencing javascript calls when the appropriate things are loaded.
多个异步事物之间没有可预测的顺序。如果需要一个可预测的顺序,则必须通过从异步脚本注册加载通知并在加载适当的内容时手动对 javascript 调用进行排序来对其进行编码。
When a script tag is inserted dynamically, how the execution order behaves will depend upon the browser. You can see how Firefox behaves in this reference article. In a nutshell, the newer versions of Firefox default a dynamically added script tag to async unless the script tag has been set otherwise.
当动态插入脚本标签时,执行顺序的行为将取决于浏览器。您可以在这篇参考文章中了解 Firefox 的行为。简而言之,新版本的 Firefox 默认将动态添加的 script 标签设为异步,除非另外设置了 script 标签。
A script tag with async
may be run as soon as it is loaded. In fact, the browser may pause the parser from whatever else it was doing and run that script. So, it really can run at almost any time. If the script was cached, it might run almost immediately. If the script takes awhile to load, it might run after the parser is done. The one thing to remember with async
is that it can run anytime and that time is not predictable.
一个脚本标签async
可以在加载后立即运行。事实上,浏览器可能会暂停解析器正在执行的任何其他操作并运行该脚本。所以,它几乎可以在任何时候运行。如果脚本被缓存,它可能几乎立即运行。如果脚本需要一段时间加载,它可能会在解析器完成后运行。要记住的一件事async
是它可以随时运行,而且时间是不可预测的。
A script tag with defer
waits until the entire parser is done and then runs all scripts marked with defer
in the order they were encountered. This allows you to mark several scripts that depend upon one another as defer
. They will all get postponed until after the document parser is done, but they will execute in the order they were encountered preserving their dependencies. I think of defer
like the scripts are dropped into a queue that will be processed after the parser is done. Technically, the browser may be downloading the scripts in the background at any time, but they won't execute or block the parser until after the parser is done parsing the page and parsing and running any inline scripts that are not marked defer
or async
.
使用脚本标签defer
等待,直到整个解析器完成,然后运行标有所有脚本defer
的顺序,他们遇到。这允许您将多个相互依赖的脚本标记为defer
. 它们都将被推迟到文档解析器完成之后,但它们将按照遇到的顺序执行,并保留它们的依赖关系。我认为defer
脚本被放入一个队列,在解析器完成后将被处理。从技术上讲,浏览器可能随时在后台下载脚本,但在解析器完成页面解析并解析和运行任何未标记defer
或 的内联脚本之前,它们不会执行或阻止解析器async
。
Here's a quote from that article:
这是那篇文章的引述:
script-inserted scripts execute asynchronously in IE and WebKit, but synchronously in Opera and pre-4.0 Firefox.
插入脚本的脚本在 IE 和 WebKit 中异步执行,但在 Opera 和 4.0 之前的 Firefox 中同步执行。
The relevant part of the HTML5 spec (for newer compliant browsers) is here. There is a lot written in there about async behavior. Obviously, this spec doesn't apply to older browsers (or mal-conforming browsers) whose behavior you would probably have to test to determine.
HTML5 规范的相关部分(适用于较新的兼容浏览器)在这里。那里有很多关于异步行为的文章。显然,此规范不适用于您可能必须测试以确定其行为的旧浏览器(或不符合标准的浏览器)。
A quote from the HTML5 spec:
引用 HTML5 规范:
Then, the first of the following options that describes the situation must be followed:
If the element has a src attribute, and the element has a defer attribute, and the element has been flagged as "parser-inserted", and the element does not have an async attributeThe element must be added to the end of the list of scripts that will execute when the document has finished parsing associated with the Document of the parser that created the element.
The task that the networking task source places on the task queue once the fetching algorithm has completed must set the element's "ready to be parser-executed" flag. The parser will handle executing the script.
If the element has a src attribute, and the element has been flagged as "parser-inserted", and the element does not have an async attributeThe element is the pending parsing-blocking script of the Document of the parser that created the element. (There can only be one such script per Document at a time.)
The task that the networking task source places on the task queue once the fetching algorithm has completed must set the element's "ready to be parser-executed" flag. The parser will handle executing the script.
If the element does not have a src attribute, and the element has been flagged as "parser-inserted", and the Document of the HTML parser or XML parser that created the script element has a style sheet that is blocking scriptsThe element is the pending parsing-blocking script of the Document of the parser that created the element. (There can only be one such script per Document at a time.)
Set the element's "ready to be parser-executed" flag. The parser will handle executing the script.
If the element has a src attribute, does not have an async attribute, and does not have the "force-async" flag setThe element must be added to the end of the list of scripts that will execute in order as soon as possible associated with the Document of the script element at the time the prepare a script algorithm started.
The task that the networking task source places on the task queue once the fetching algorithm has completed must run the following steps:
If the element is not now the first element in the list of scripts that will execute in order as soon as possible to which it was added above,then mark the element as ready but abort these steps without executing the script yet.
Execution: Execute the script block corresponding to the first script element in this list of scripts that will execute in order as soon as possible.
Remove the first element from this list of scripts that will execute in order as soon as possible.
If this list of scripts that will execute in order as soon as possible is still not empty and the first entry has already been marked as ready, then jump back to the step labeled execution.
If the element has a src attributeThe element must be added to the set of scripts that will execute as soon as possible of the Document of the script element at the time the prepare a script algorithm started.
The task that the networking task source places on the task queue once the fetching algorithm has completed must execute the script block and then remove the element from the set of scripts that will execute as soon as possible.
OtherwiseThe user agent must immediately execute the script block, even if other scripts are already executing.
然后,必须遵循以下描述情况的选项中的第一个:
如果元素有 src 属性,并且该元素有 defer 属性,并且该元素已被标记为“解析器插入”,并且该元素没有 async 属性该元素必须添加到列表的末尾当文档完成与创建元素的解析器的文档相关联的解析时将执行的脚本。
一旦获取算法完成,网络任务源放置在任务队列上的任务必须设置元素的“准备好被解析器执行”标志。解析器将处理执行脚本。
如果该元素具有 src 属性,并且该元素已被标记为“解析器插入”,并且该元素没有 async 属性该元素是创建该元素的解析器的 Document 的挂起解析阻止脚本。(每个文档一次只能有一个这样的脚本。)
一旦获取算法完成,网络任务源放置在任务队列上的任务必须设置元素的“准备好被解析器执行”标志。解析器将处理执行脚本。
如果该元素没有 src 属性,并且该元素已被标记为“已插入解析器”,并且创建该脚本元素的 HTML 解析器或 XML 解析器的文档具有阻止脚本的样式表,则该元素是创建元素的解析器的文档的暂挂解析阻止脚本。(每个文档一次只能有一个这样的脚本。)
设置元素的“准备好被解析器执行”标志。解析器将处理执行脚本。
如果元素有 src 属性,没有 async 属性,并且没有设置“force-async”标志必须将元素添加到将按顺序执行的脚本列表的末尾关联在准备脚本算法开始时使用脚本元素的文档。
一旦获取算法完成,网络任务源放置在任务队列中的任务必须运行以下步骤:
如果该元素现在不是脚本列表中将尽快按顺序执行的第一个元素(如上面添加的那样),则将该元素标记为就绪,但在不执行脚本的情况下中止这些步骤。
执行:执行此脚本列表中第一个脚本元素对应的脚本块,将尽快按顺序执行。
从此脚本列表中删除将尽快按顺序执行的第一个元素。
如果这个将尽快按顺序执行的脚本列表仍然不为空并且第一个条目已经标记为就绪,则跳回标记为执行的步骤。
如果元素具有 src 属性,则必须将元素添加到脚本集,该脚本集将在准备脚本算法开始时尽快执行脚本元素的文档。
一旦获取算法完成,网络任务源放置在任务队列中的任务必须执行脚本块,然后从将尽快执行的脚本集中移除元素。
否则用户代理必须立即执行脚本块,即使其他脚本已经在执行。
What about Javascript module scripts, type="module"
?
Javascript 模块脚本type="module"
呢?
Javascript now has support for module loading with syntax like this:
Javascript 现在支持使用以下语法加载模块:
<script type="module">
import {addTextToBody} from './utils.mjs';
addTextToBody('Modules are pretty cool.');
</script>
Or, with src
attribute:
或者,使用src
属性:
<script type="module" src="http://somedomain.com/somescript.mjs">
</script>
All scripts with type="module"
are automatically given the defer
attribute. This downloads them in parallel (if not inline) with other loading of the page and then runs them in order, but after the parser is done.
所有具有 的脚本都会type="module"
自动赋予该defer
属性。这会与页面的其他加载并行(如果不是内联)下载它们,然后按顺序运行它们,但在解析器完成之后。
Module scripts can also be given the async
attribute which will run inline module scripts as soon as possible, not waiting until the parser is done and not waiting to run the async
script in any particular order relative to other scripts.
模块脚本也可以被赋予一个async
属性,该属性将尽快运行内联模块脚本,而不是等到解析器完成,也不等待以async
相对于其他脚本的任何特定顺序运行脚本。
There's a pretty useful timeline chart that shows fetch and execution of different combinations of scripts, including module scripts here in this article: Javascript Module Loading.
有一个非常有用的时间线图表,显示了不同脚本组合的获取和执行,包括本文中的模块脚本:Javascript 模块加载。
回答by Niet the Dark Absol
The browser will execute the scripts in the order it finds them. If you call an external script, it will block the page until the script has been loaded and executed.
浏览器将按照它找到的顺序执行脚本。如果调用外部脚本,它将阻塞页面,直到脚本加载并执行完毕。
To test this fact:
要测试这个事实:
// file: test.php
sleep(10);
die("alert('Done!');");
// HTML file:
<script type="text/javascript" src="test.php"></script>
Dynamically added scripts are executed as soon as they are appended to the document.
动态添加的脚本一旦添加到文档中就会立即执行。
To test this fact:
要测试这个事实:
<!DOCTYPE HTML>
<html>
<head>
<title>Test</title>
</head>
<body>
<script type="text/javascript">
var s = document.createElement('script');
s.type = "text/javascript";
s.src = "link.js"; // file contains alert("hello!");
document.body.appendChild(s);
alert("appended");
</script>
<script type="text/javascript">
alert("final");
</script>
</body>
</html>
Order of alerts is "appended" -> "hello!" -> "final"
警报顺序是“附加”->“你好!” - >“最终”
If in a script you attempt to access an element that hasn't been reached yet (example: <script>do something with #blah</script><div id="blah"></div>
) then you will get an error.
如果在脚本中您尝试访问尚未到达的元素(例如:),<script>do something with #blah</script><div id="blah"></div>
那么您将收到错误消息。
Overall, yes you can include external scripts and then access their functions and variables, but only if you exit the current <script>
tag and start a new one.
总的来说,是的,您可以包含外部脚本,然后访问它们的函数和变量,但前提是您退出当前<script>
标签并开始一个新标签。
回答by Dehan de Croos
A great summary by @addyosmani
@addyosmani的精彩总结
Shamelessly copied from https://addyosmani.com/blog/script-priorities/
回答by Flion
After testing many options I've found that the following simple solution is loading the dynamically loaded scripts in the order in which they are added in all modern browsers
在测试了许多选项后,我发现以下简单的解决方案是按照在所有现代浏览器中添加的顺序加载动态加载的脚本
loadScripts(sources) {
sources.forEach(src => {
var script = document.createElement('script');
script.src = src;
script.async = false; //<-- the important part
document.body.appendChild( script ); //<-- make sure to append to body instead of head
});
}
loadScripts(['/scr/script1.js','src/script2.js'])