【问题标题】:How Javascript is getting executed in browser by Javascript Engine?Javascript 引擎如何在浏览器中执行 Javascript?
【发布时间】:2014-02-28 20:38:29
【问题描述】:

问题不是解决方案,问题是为了更好地了解系统

专家!我知道每当您将 javascript 代码输入 javascript 引擎时,它都会立即由 javascript 引擎执行。由于没有看过Engine的源码,所以有几个问题如下,

假设我正在从远程服务器加载几个文件,即 FILE_1.js 和 FILE_2.js。 并且 FILE_2.js 中的代码需要 FILE_1.js 中的一些代码。所以我包含了如下文件,

<script type="text/javascript" src="FILE_1.js" ></script>
<script type="text/javascript" src="FILE_2.js" ></script>

希望我已经完成了 Javascript 引擎所需的工作。不幸的是,我在 FILE_1.js 中编写了 5000KB 的代码,但是我在 FILE_2.js 中有 5KB 的代码。由于服务器是多线程的,所以 FILE_2.js 肯定会在 FILE_1.js 完成之前加载到我的浏览器中。

javascript 引擎如何处理这个问题?

如果将代码从 FILE_2.js 移动到 inline-script 标签,如下所示,javascript 引擎采取了哪些措施来管理这种依赖关系?

<script type="text/javascript" src="FILE_1.js" ></script>
<script type="text/javascript" >
// Dependent code goes here
</script>

注意:我不希望单字回答单线程。我只想深入了解谁在管理浏览器或javascript引擎或普通人的发出请求?如果请求/响应是由普通人处理的,那么 javascript 引擎如何意识到这一点?

【问题讨论】:

  • 服务器将等待文件 1 完全加载后再执行文件 2,因为文件 2 可能依赖于它。如果它们是独立的,您始终可以使用模块加载器,或将 defer 属性添加到脚本中。
  • @BenjaminGruenbaum 是正确的,只要您按该顺序加载它们。也就是说,我希望 5000KB 是虚构的数字;这对于客户端脚本来说是巨大的

标签: javascript javascript-engine


【解决方案1】:

当我发布关于代码行为的答案时,我总是喜欢去两个地方:

  1. 规范
  2. 实施

规格:

DOM API 明确规定脚本必须按顺序执行:

如果元素有 src 属性,没有 async 属性,并且没有设置“force-async”标志 在准备脚本算法开始时,该元素必须添加到与脚本元素的文档相关联的将尽快按顺序执行的脚本列表的末尾

来自4.1 Scripting。请在使用 deferasync 属性之前检查此规则的例外列表。这在4.12.1.15 中有很好的说明。

这是有道理的,想象一下:

 //FILE_1.js
     var trololo = "Unicorn";
     ....
     // 1 million lines later
     trololo = "unicorn";
     var message = "Hello World";
//FILE_2.js
     alert(message); // if file 1 doesn't execute first, this throws a reference error.

通常最好使用模块加载器(它将延迟脚本插入和执行,并为您正确管理依赖项)。

目前,最好的方法是使用 BrowserifyRequireJS 之类的东西。将来,我们将能够使用 ECMAScript 6 模块。

实施:

嗯,你提到了它,我无法抗拒。所以,如果我们检查 Chromium blink 源(在 WebKit 中仍然类似):

bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition, 
                                    LegacyTypeSupport supportLegacyTypes)
    {
    .....
    } else if (client->hasSourceAttribute() && // has src attribute
              !client->asyncAttributeValue() &&// and no `async` or `defer`
              !m_forceAsync                    // and it was not otherwise forced                                   
              ) { // - woah, this is just like the spec
   m_willExecuteInOrder = true; // tell it to execute in order
   contextDocument->scriptRunner()->queueScriptForExecution(this, 
                                                            m_resource,
                                      ScriptRunner::IN_ORDER_EXECUTION);

太好了,所以我们可以在源代码中看到它按解析顺序添加它们 - 就像规范所说的那样。

让我们看看script runner does

void ScriptRunner::queueScriptForExecution(ScriptLoader* scriptLoader,
                                          ResourcePtr<ScriptResource> resource,
                                          ExecutionType executionType){
     .....
     // Adds it in the order of execution, as we can see, this just 
     // adds it to a queue
     case IN_ORDER_EXECUTION:
        m_scriptsToExecuteInOrder.append(PendingScript(element, resource.get()));
        break;
     }

并且,使用计时器,它会在准备好时(或立即,如果没有任何待处理)触发它们:

 void ScriptRunner::timerFired(Timer<ScriptRunner>* timer)
 {
 ...
    scripts.swap(m_scriptsToExecuteSoon);
    for (size_t i = 0; i < size; ++i) {
    ....
        //fire!
        toScriptLoaderIfPossible(element.get())->execute(resource);
        m_document->decrementLoadEventDelayCount();
    }

【讨论】:

  • 由于 ECMAScript 6 已经发布,您能否更新这个答案? TIA
  • 这有什么关系?这是与 DOM API 不同的规范
  • 既然您指出最好使用模块加载器,并且您说可以使用 ES6 来实现它,我想了解更多
  • @Mark ECMAScript 6 模块目前还没有准备好生产(很遗憾)——它们可以工作,但人们大部分时间仍然使用 webpack(这是理所当然的)
  • @BenjaminGruenbaum 为什么 es6 模块还没有准备好生产?所有主流浏览器都支持它,除了 IE。如果您不关心 IE,我会说使用 es6 模块是安全的。
猜你喜欢
  • 2014-02-27
  • 1970-01-01
  • 2016-06-24
  • 2015-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-29
相关资源
最近更新 更多