【问题标题】:Async code executed between <script> tags<script> 标签之间执行的异步代码
【发布时间】:2018-02-02 11:46:18
【问题描述】:

我是一名 NodeJs 开发人员,碰巧有一些 Web 应用程序。 我非常熟悉异步代码以及事件循环的工作原理。但是,我一直面临一个无法解决的问题,直到我意识到异步代码在被拆分为多个脚本标签时可能表现不同。 情况如下

  • 我在头部有一个带有一些异步代码的脚本标签。这样的代码期望某些功能存在。该函数应以同步方式在第二个脚本标记上声明,因此在异步内容完成时该函数应该存在。
  • 第二个脚本标签是 100% 同步的,它在代码中间创建了一个名为 boot 的函数。

让我用一些简化的代码来说明它:

// <script>
 somePromise()
    .then( ()=> window.boot())
// </script>
// < ... some html code and body >
// <script>
window.boot = function (){}
// </script>

由于 promise 回调应该异步执行,我希望它进入事件循环并允许其余的同步代码执行。

在某些浏览器上,这符合我的预期。当我说一些浏览器时,我是指不同用户在不同计算机上的浏览器,因为即使使用相同的浏览器品牌和版本,行为也会有所不同。但是,在某些情况下,promise 回调在第二个脚本标签有机会启动并引发错误之前执行。

这应该如何在浏览器上工作?是否每个脚本标签都执行到所有代码都执行完毕,甚至是异步的?如果是这样,为什么它适用于其他一些浏览器?

提前致谢。

【问题讨论】:

  • 所有脚本都应该在body标签的底部(被广泛接受的约定)。尝试做同样的事情。
  • 第二个脚本是否使用脚本标签中的src 属性加载?
  • @traktor53 两个脚本都包含要执行的实际代码。
  • @waleed-lqbal 我正在使用一个已经以这种方式构建的框架,无论如何我的问题不是关于如何解决问题,我有几种方法来解决它,它是关于理解实际行为。
  • 因为有时承诺会在与网络和解析器的竞争中获胜(失败?),并在其他代码准备好之前执行。为什么要在没有安全检查和重试窗口的情况下首先创建这种竞争条件?

标签: javascript html asynchronous


【解决方案1】:

HTML5 解析器不是同步的。

这是我自己改写的部分HTML Parser Threading on MDN 的摘录。正式处理和重读请参考原文。

脚本标签 [pairs] 之间的 HTML 由在主线程外执行的 nsHtml5StreamParser 解析。

解析器运行自己的事件循环,并响应来自主线程的请求以执行onDataAvailableOnStopRequest处理。分别处理调用DoDataAvailabledoStopRequst

DoDataAvailable 输出小的 DOM 树操作,例如将元素创建到主线程中处理的队列中。树操作队列不时刷新到主线程**使用计时器。**

刷新后,解析器向主线程分派一个可运行的对象来处理树的添加。主线程中的runner(或executor)调用nsHtml5TreeOpExecutor::RunFlushLoop().

@KarelG 在this question 中的评论说,网络数据通常以 8Kb 块处理。这个问题很值得一读。

因此,JavaScript 事件循环有时有机会在第二个脚本元素被解析和执行之前履行承诺并执行 onFulfilled 处理程序 - 正如您已经发现的那样!

总而言之,网络检索源代码的变幻莫测,使用计时器来发起树构建操作处理的异步 HTML 解析,以及树构建器可能调用或可能不调用的任何进一步异步操作,都组合在一起创建一个竞争条件。

window.boot在调用时未定义时,竞态条件的失败分支的不可预测性很可能是由于浏览器品牌、网络速度、设备利用率、HTML内容的长度和类型的综合影响,以及HTML 解析器计时和计时器。

显而易见的结论是,代码一开始就不应该设置这种竞争条件。这是不安全的编码习惯。谢天谢地,您可以解决它。

【讨论】:

  • 这是一个很棒的答案。非常感谢你。正如我所说,我找到了一个解决方案,但是有了这个解释(以及它的参考资料)完全满足了我的好奇心。比赛条件肯定是邪恶的,但有时你甚至没有意识到它们的存在。
【解决方案2】:

实际上,IE 不支持 Promise,所以你可以使用一些外部库,比如 bluebirdJS,或者你可以使用转译器(babel 或其他)

【讨论】:

    猜你喜欢
    • 2017-09-25
    • 1970-01-01
    • 2019-05-18
    • 1970-01-01
    • 2018-01-25
    • 2019-01-28
    • 2018-12-08
    • 1970-01-01
    相关资源
    最近更新 更多