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