异步Javascript程序如何交互

时间:2016-12-15 17:34:02

标签: javascript

早在网络开发的早期,我就获得了一些民间智慧,对于像这样的代码

<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.jsprogram2.js时,javascript会向事件队列添加一条消息,并且当javascript引擎在事件循环中运行时,将处理该消息。

对我来说缺少的是 - 这条消息说的是什么?是每个程序的单个消息,说“编译并执行所有这些javascript代码”?或者每个程序都会创建多条消息 - 在我看来可能看起来像

  • 消息1:从该程序中提取所有函数并进行编译
  • 消息2:从此程序中提取全局范围内的所有语句和表达式
  • 消息3-n:将每个语句和表达式添加为单独的队列消息,以便以后处理。

当浏览器正在处理program1.js时会发生什么,但是完成下载program2.js?是否有可能交错执行每个程序的语句?

我意识到,作为客户端开发人员,这里的最佳实践是不依赖于全局范围并编写每个程序和函数,因此调用它的方式并不重要,并且不会阻止其他人的代码。但是,我花了很多时间处理其他人的代码,其中一些代码表现不佳。我想了解幕后发生的事情,或者这是否是未定义的行为,与引擎无关,并且不会在实现之间排列。

2 个答案:

答案 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;指示浏览器执行的操作是:

  1. 尽早开始下载此资源。
  2. 继续前进并随意下载其他资源。
  3. 但是一旦我的下载完成,就停止渲染并尽快开始执行。
  4. 有效地&#34; async&#34;仍然可以阻止渲染和后续执行,但它允许解析器继续工作直到执行开始。

    多个异步脚本无法保证它们将执行的顺序。取决于加载的速度。这使得像RequireJS这样的AMD系统可以定义依赖关系和回调来加载资源异步,但是将它们的执行排队直到&#34; global&#34;环境包含所有先决条件,并且执行顺序可以通过符文(通过符文魔法)进行协商。

    推迟

    Defer表现得像这样:

    1. 尽早开始下载此资源。
    2. 继续前进并随意下载其他资源。
    3. 但是一旦我的下载完成,在解析器完成之前什么都不做。
    4. 现在按照发现的顺序执行所有脚本。
    5. 一方面,&#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。假设解析脚本成功,它将在一次运行中实例化所有声明并评估脚本体。