How Javascript is getting executed in browser by Javascript Engine?

混江龙づ霸主 提交于 2019-12-03 06:50:20

When I post an answer about the behavior of code, I always like to go to two places:

  1. The specification
  2. The implementation

The specification:

The DOM API explicitly specifies scripts must be executed in order:

If the element has a src attribute, does not have an async attribute, and does not have the "force-async" flag set The element must be added to the end of the list of scripts that will execute in order as soon as possible associated with the Document of the script element at the time the prepare a script algorithm started.

From 4.1 Scripting. Please check the list of exceptions to this rule before - having the defer or async attribute. This is specified well in 4.12.1.15.

This makes sense, imagine:

 //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.

It is generally better to use a module loader (that will defer script insertion and execution, and will manage dependencies correctly for you).

At the moment, the best approach is to use something like Browserify or RequireJS . In the future, we'll be able to use ECMAScript 6 modules.

The implementation:

Well, you mentioned it and I couldn't resist. So, if we check the Chromium blink source (still similar in 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);

Great, so we can see in the source code that it adds them in order parsed - just like the specification says.

Let's see how a 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;
     }

And, using a timer, it fires them one by one when ready (or immediately, if nothing is pending):

 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();
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!