When using async and Defer, is the order of the scripts respected?

Asked

Viewed 1,130 times

29

For a single script, the load/run order in presence or not of attributes async and defer is clear: with nothing, loads and executes immediately, with async loads in parallel and performs at the end of the load, with defer loads in parallel and executes at the end of rendering:

My doubt is what happens when there is more than one script on the page, especially if the use of the above attributes is combined:

<script src="A.js" defer></script> <!-- Grande -->
<script src="B.js" defer></script> <!-- Pequeno -->
<script src="C.js" async></script> <!-- Grande -->
<script src="D.js" async></script> <!-- Pequeno -->
<script src="E.js"></script>       <!-- Grande -->
<script src="F.js"></script>       <!-- Pequeno -->

By my understanding, one can make some statements with some certainty (correct me if I’m wrong):

  • And will execute before F;
  • A and B will execute after E and F;
  • C and D can perform before or after E and F (including between);
  • D can perform before C if its charge is faster.

But for others I don’t know the answer:

  • B can perform before A, if your charge is faster? Or the defer imposes an order on other scripts also with defer?
  • If D is loaded while E is loading (but not running), Will he execute before E or not? (i.e. when the load of a synchronous script has started, it is necessary to wait for its execution to finish to execute an asynchronous script?)
  • C and D can run after A and B? In other words, if the C load for example takes too long, and both the page rendering and the A and B load complete first, these can run immediately, or it is necessary that all scripts async execute before any script defer execute?

I would like to know if possible what is specified regarding the order of execution of the scripts and what may vary with the implementation (no need to test, although these are welcome as a complement).

  • 1
  • da uma lida aqui http://answall.com/a/46475/12032

  • 1

    In the response of re22 says he has no way of knowing the execution of defers. But here I understand it’s in order of occurrence.

  • @Sneepsninja I had already read this answer, but it did not clarify all my doubts. Perhaps some of your external references answered, but I missed a more direct answer with regard to order, hence the question. Also, I read some conflicting references (ex.: this site says "If the script relies upon or is relied upon by Another script then use Defer", but this contradicts the linked answer, which says "there is no way of knowing in which order they will be executed"; which is right? Etc.).

  • As far as I have read, my English is well +- so I may be mistaken, Defer "was" to ensure the order of the post-load scripts of the page but in practice it does not guarantee http://stackoverflow.com/questions/5250412/how-exactly-does-script-defer-work

  • 1

    In practice it is recommended to compress everything into a single script, both to decrease latency and to avoid these "issues of order". Then you can use Defer and async at the same time.

  • @epx Sure, but even though this "one size fits all" solution covers 95% of cases, it is far from perfect. As I have already commented in that other answer, where you put the script has an impact on the user experience (e.g., waiting longer and seeing "beautiful" content, or seeing "raw" content faster and unexpectedly seeing it change in response to the script, if only CSS is not enough), and unifying complicates the use of Cdns. Knowing more about the subject gives us more tools to get closer to the ideal outcome for each particular case.

  • 1

    @Sneepsninja Since no one answered, I did some research, some tests, and gave a self answer. In fact, in HTML 4 the order of deferwas not specified, and browsers could execute them in the order they wanted (as the answer you linked - 2011 - also states). HTML5 imposes yes an order, and the browsers modern apparently respect it.

Show 3 more comments

1 answer

8


TL;DR - async execute at any time and in any order, defer and "normal" execute in the order they are declared (but all defer after all the "normal").

Find an accurate specification of the behavior of async and defer is complicated, since they have influence in several stages of processing the browser. According to the specification of HTML5:

Note: The exact processing details for these attributes are, primarily for historical reasons, somewhat non-trivial, involving a number of aspects of HTML. The implementation requirements are therefore by necessity spread across the specification. The low algorithms (in this section) describe the core of this processing, but these algorithms reference and are referenced by the rules of Parsing for the tags script of beginning and end in HTML, in foreign content, and in XML, the rules for the method document.write(), the handling of the scripting, etc..

Some of the question points are responsive by specification, however:

B can perform before A, if your charge is faster? Or the defer imposes an order on other scripts also with defer?

No, the order is respected. According to this same specification, there is a list of scripts to be executed when the document Parsing, and each script with defer should be included at the end of that list:

To prepare the script, the user agent must Act as Follows:

  1. Then, the first of the following options that describes the Situation must be Followed:

    • If the element has a src attribute, and the element has a defer attribute, and the element has been flagged as "parser-inserted", and the element does not have an async attribute

      The element must be Added to the end of the list of scripts that will execute when the Document has finished Parsing Associated with the Document of the parser that created the element.

That way, if a browser acts in accordance with that specification (see tests below) and executes the scripts in the order they were inserted into that list (i.e. the order in which it appears in the source code) so if I load, say, the jQuery of a CDN, the bootstrap of another, and my own server code, all with defer, they must execute in that same order, so that dependence on one another must be satisfied.

<script src="//code.jquery.com/jquery-2.1.4.min.js" defer></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js" defer></script>
<script src="/js/meucodigo.min.js" defer></script>

But attention: If the browser does not give proper support to HTML5, scripts defer can yes perform out of order! According to html 4 specification, the defer is just a "hint" to browser that the script will not produce any content to the document (e.g.: via document.write) and so he can continue to do the parse and rendering. Nothing else is specified, so it is safe to assume that the execution order may vary from implementation to implementation.

If D is loaded while E is loading (but not running), it will execute before E or not?

I think yes. The specification applicable to items with async (same item 15 above) says the following:

The task that the task source networking Places on the task Queue Once the fetching Algorithm has completed must execute the script block and then remove the element from the set of scripts that will execute as soon as possible.

In contrast, the elements without async have a different behaviour:

The element is the pending Parsing-blocking script of the Document of the parser that created the element. (There can only be one such script per Document at a time.)

The task that the task source networking Places on the task Queue Once the fetching Algorithm has completed must set the element’s "ready to be parser-executed" flag. The parser will Handle executing the script.

That is, when the browser finds one or more scripts with async (ex.: C and D), he places them in the task queue and moves on. When he finds a without async (nor defer) it also puts you in the job queue, but marks it as "blocker Parsing". I don’t quite understand what that means (because the process of Parsing in general it is described in another document - I’m not sure which), but in practice it should serve to interrupt the rendering (but not necessarily the load) until the script has just run.

However, it is unclear whether the presence of a "blocker script Parsing" is or is not hindering the task of completing scripts async ("execute the script block") is executed in that state. When in doubt, I will adopt a conservative stance and assume that "no" (i.e. I will work with the hypothesis that after the load of E begin - but before its execution - C and D may perform).

The tests below seem to corroborate this my interpretation.

C and D can run after A and B? (...) or it is necessary that all async scripts run before any Defer script runs?

Yes, they can execute at any time. I couldn’t find anything in the specification that would prohibit scripts in the list of "scripts to be executed as soon as possible" from running after scripts in the list of "scripts to be executed when the document finishes the Parsing". In fact, a simple test in Chrome (without webserver, in the file system itself) came to order "C,E,F,A,B,D" in one of the tests, and "E,F,A,B,C,D" in others.

Testing

To test the actual behavior of browsers, I made a small change in the SimpleHTTPServer python:

import SimpleHTTPServer, SocketServer, sys, time
from SimpleHTTPServer import SimpleHTTPRequestHandler

class MeuHTTPServer(SimpleHTTPRequestHandler):
    def do_GET(*args, **kwargs):
        time.sleep(int(sys.argv[2]))
        return SimpleHTTPRequestHandler.do_GET(*args, **kwargs)
        
SocketServer.TCPServer(("", int(sys.argv[1])), MeuHTTPServer).serve_forever()

And I opened the program 7 times, one to serve each file (the 6 scripts and the html itself). "Large" scripts (A,C,E) wait 5 seconds before returning, "small" scripts (B,D,F), 1 second. The html used was:

<html>
<body>
<script src="http://localhost:8001/a.js" defer></script>
<script src="http://localhost:8002/b.js" defer></script>
<script src="http://localhost:8003/c.js" async></script>
<script src="http://localhost:8004/d.js" async></script>
<script src="http://localhost:8005/e.js"></script>
<script src="http://localhost:8006/f.js"></script>
</body>
</html>

And each script just makes one console.log to "identify themselves". The results (in Windows 7) were:

  • Firefox: "D,E,F,A,B,C", showing all the points cited (including the request GET of all the scripts were made before of d run - confirming a script async can perform while a "normal" is charging). In another test, "D,C,E,F,A,B" (also consistent with the rest of the answer). After that, "C,D,E,F,A,B" after the scripts enter the cache.
  • Chrome: "D,C,E,F,A,B" in 2 tests, "E,F,A,B,C,D" after the scripts entered the cache (I noticed a trend of the scripts async be executed lastly - even after the defer - unlike Firefox, which tends to run them in order).
  • Internet Explorer 11: "D,E,C,F,A,B" (demonstrating that a script async in fact can run between two "normal") the first time, "D,C,E,F,A,B" in the following (apparently the IE does not put resources from the localhost cached).
  • Opera and Safari: "D,C,E,F,A,B" in 1 test, "E,F,A,B,C,D" in the others (same as Chrome).

My conclusion is that the interpretation I gave to the specification is correct, and the main ones browsers comply with that specification.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.