Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async的用法是不是有问题? #1

Open
rcocco opened this issue Feb 27, 2019 · 1 comment
Open

async的用法是不是有问题? #1

rcocco opened this issue Feb 27, 2019 · 1 comment

Comments

@rcocco
Copy link

rcocco commented Feb 27, 2019

很多章节的js文件中都是进行操作DOM,又没有DOMContentLoaded的,而在HTML中它们几乎都是用的<script async>这种形式的标签。

<head>
    <script src="script.js" async></script>
</head>

但查其他资料却都是说 async 异步加载完立刻执行,最好不要用于上面那种操作DOM的情况:Script Tag - async & defer

而 MDN 的入门文档却说这样用是对的,比如 什么是 JavaScript? 里介绍如何避免在解析HTML文档之前调用javascript的部分:
【在上文的“内部”、“外部”示例中,JavaScript 调用于文档头处,解析 HTML 文档体之前。这样做是有隐患的,需要使用一些结构来避免错误发生。】
【“内部”示例使用了以下结构:这是一个事件监听器,它监听浏览器的 "DOMContentLoaded" 事件,即 HTML 文档体加载、解释完毕事件。事件触发时将调用 " . . ." 处的代码,从而避免了错误发生(事件 的概念稍后学习)。】
【“外部”示例中使用了 JavaScript 的一项现代技术(async “异步”属性)来解决这一问题,它告知浏览器在遇到 <script> 元素时不要中断后续 HTML 内容的加载。注:“外部”示例中 async 属性可以解决调用顺序问题,因此无需使用 DOMContentLoaded 事件。而 async 只能用于外部脚本,因此不适用于“内部”示例。】

再比如笑话生成器 的示例代码。

@roy-tian
Copy link
Owner

感谢 @rcocco 朋友提出的问题,这里 async 的用法是不严谨的。

以下是对 deferasync 的简短的讨论,但不作为代码中不严谨(甚至错误)内容的托辞。

仅考虑单一一个脚本的情形,若将其放置在 </body> 前,那么页面就将按着“解析 HTML -> 下载脚本 -> 执行脚本”的顺序依次进行。

若将一个 async 脚本放在 HTML 头部,那么加载顺序是这样的:

  • 开始解析 HTML,
  • 遇到 <script async ... > 时开始下载脚本,下载脚本的过程不影响 HTML 的解析,
  • 脚本下载完毕后,暂停解析 HTML 并开始执行脚本,
  • 待脚本执行完毕后继续解析 HTML 直至完成。

若将一个 defer 脚本放在 HTML 头部,加载顺序如下:

  • 开始解析 HTML,
  • 遇到 <script defer ...> 时开始下载脚本,下载脚本的过程不影响 HTML 的解析,
  • 脚本在下载完毕后并不立即执行,而是等待 HTML 解析完毕后再执行。

async 脚本问题关键就在于,它的执行会阻塞 HTML 的解析,因此,虽然脚本的下载可以与 HTML 解析同时进行,一旦脚本加载完毕准备运行时 DOM 尚未加载完毕,并且恰巧这个脚本又操作了某个尚未加载的元素,错误就会发生。

之所以示例没有显性的错误,是因为 这些 HTML 的 body 都很短小,很大概率会在脚本下载完毕之前完成解析。但是隐患是存在的。

上面的描述很抽象,@rcocco 同学引用的 SO 帖子 中 Prasanth Bendra 的答案中通过几个图片非常直观地显示了 asyncdefer 脚本的加载执行过程。这里有一篇 文章 说的也很明了,值得参考。

另外,上述 SO 帖子 中 jfriend00 (即被采纳的)的答案中观点是:“永远把脚本放在 </body> 之前”,这种说法在今天看来有些绝对了,defer 提升性能的功效是可见的,而且不支持这些特性的所谓“旧浏览器”都是 IE9、Firefox3、Chrome7 之类,份额很小了。

顺便推荐一个很好的检测一些特性是否可用的 网站

上述 文章 的结论“多用 head 中的 defer 脚本,可提高页面加载效率”值得参考,但也不是绝对。

代码稍后修复,再次感谢 @rcocco,欢迎各位对各种问题批评指正。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants