早在网络开发的早期,我就获得了一些民间智慧,对于像这样的代码
<script src=".../program1.js"></script>
<script src=".../program2.js"></script>
浏览器会暂停,加载javascript,编译,执行,继续下一个script
标记,然后重复。通过这种方式,浏览器可以在页面上的所有javascript中运行,并将其视为一个线性程序。
然而,在勇敢的新现代javascript世界中,我们通过async
属性进行异步加载
<script src=".../program1.js" async></script>
<script src=".../program2.js" async></script>
我的理解是这是好事,因为现在浏览器不需要暂停,下载脚本并执行它。相反,它开始下载脚本,但将继续解析DOM。即等待javascript下载时网页不再阻塞。 (如果这不是真的,我会很感激修正)。
但是,不太清楚(并且更难测试)是这两个程序如何相互作用。它们似乎在相同的共享空间中运行(即,从用户角度来看,javascript仍然是单线程的,具有两个(全局,函数)范围)。但是,在我阅读的文档中,它们执行的顺序似乎不明确。
我已阅读有关Concurrency model and Event Loop的MDN文章。虽然有趣且有用,但它并没有完全回答我的问题。根据我收集的内容,当浏览器加载program1.js
或program2.js
时,javascript会向事件队列添加一条消息,并且当javascript引擎在事件循环中运行时,将处理该消息。
对我来说缺少的是 - 这条消息说的是什么?是每个程序的单个消息,说“编译并执行所有这些javascript代码”?或者每个程序都会创建多条消息 - 在我看来可能看起来像
当浏览器正在处理program1.js
时会发生什么,但是完成下载program2.js
?是否有可能交错执行每个程序的语句?
我意识到,作为客户端开发人员,这里的最佳实践是不依赖于全局范围并编写每个程序和函数,因此调用它的方式并不重要,并且不会阻止其他人的代码。但是,我花了很多时间处理其他人的代码,其中一些代码表现不佳。我想了解幕后发生的事情,或者这是否是未定义的行为,与引擎无关,并且不会在实现之间排列。
答案 0 :(得分:7)
有两篇文章说明&#34; async&#34;和&#34;推迟&#34;实际意义上的属性(我在浏览器内部的绿色):
从2014年开始,优秀+简单的图形: http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html
从2016年起为什么异步可能是一种反模式: http://calendar.perfplanet.com/2016/prefer-defer-over-async/
对于你的问题,&#34; async&#34;指示浏览器执行的操作是:
有效地&#34; async&#34;仍然可以阻止渲染和后续执行,但它允许解析器继续工作直到执行开始。
多个异步脚本无法保证它们将执行的顺序。取决于加载的速度。这使得像RequireJS这样的AMD系统可以定义依赖关系和回调来加载资源异步,但是将它们的执行排队直到&#34; global&#34;环境包含所有先决条件,并且执行顺序可以通过符文(通过符文魔法)进行协商。
Defer表现得像这样:
一方面,&#34;推迟&#34;更快,因为它永远不会阻止解析器或渲染。但是&#34;推迟&#34;可能会变慢,因为它必须等待执行,直到管道清除。
这听起来像&#34; async&#34;总是更好,但是如果你在弱CPU手机上加载2 MB的JS并且连接速度很快,你可能会在允许解析器完成渲染之前等待10s执行。使用&#34;推迟&#34;会阻止您的交互层被延迟。
如果您正在谈论客户端/服务器或客户端应用程序,那么区别就更模糊了。在像Magento这样的后端重型应用程序中使用延迟可能更有利,其中渲染是在服务器端处理的。
在完全客户端应用程序中,您可能会获得零内容,直到加载JS monolith,因此&#34;推迟&#34;并没有真正为你做任何事,但那时也没有&#34; async&#34;如果你的整个应用程序都是一个庞大的JS软件包,那么并行无关。
答案 1 :(得分:4)
不,绝对没有。 JS仍然是单线程的,一个程序在另一个程序之后运行(尽管其中哪个程序首先可能是未知的)。是否有可能交错执行每个程序的语句?
该事件循环消息说什么?
邮件是ScriptEvaluationJob。假设解析脚本成功,它将在一次运行中实例化所有声明并评估脚本体。