3

The following snippet is taken from an example in Google API doc. The intriguing part of this snippet is that onload event handler in the two <script async> in <head> are defined later in the <body>. Does the onload event in the async script only fire after the <body> are parsed? Does any spec provide such guarantee? Or this code is only correct under the implied assumption that these two particular scripts in <head> takes a long time to fetch and execute?

<!DOCTYPE html>
<html>
<head>
  <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoad()"></script>
  <script async defer src="https://accounts.google.com/gsi/client" onload="gisInit()"></script>
</head>
<body>
  <script>

    function gapiLoad() {
       // do something 
    }

    function gisInit() {
       // do something 
    }

    // there are other stuffs...

  </script>
</body>
</html>
sgu
  • 1,301
  • 1
  • 13
  • 25
  • 1
    these two : https://stackoverflow.com/questions/40193553/load-event-on-script-with-async-and-or-defer & https://javascript.info/script-async-defer – nullqube May 03 '22 at 06:45

2 Answers2

3

No async doesn't ensure the script will be executed after the document has been parsed. It will load the script in parallel and execute it as soon as possible, so if the fetching of the script finishes before the parser reaches the <script> where your callbacks are being defined, that would be a problem indeed:

<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoad()"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisInit()"></script>
<!-- This one is blocking for at least 3s -->
<script src="https://deelay.me/3000/https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
function gapiLoad() {
   console.log("loaded");
}
function gisInit() {
   console.log("init");
}
</script>

defer does that, but if both defer and async are set, async's behavior wins, so we have to ensure only defer is set.

<script defer src="https://apis.google.com/js/api.js" onload="gapiLoad()"></script>
<script defer src="https://accounts.google.com/gsi/client" onload="gisInit()"></script>
<!-- this one is blocking for at least 3s -->
<script src="https://deelay.me/3000/https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
function gapiLoad() {
   console.log("loaded");
}
function gisInit() {
   console.log("init");
}
</script>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • @Kalido What you are think about to attach the onload event to the body tag? – Maik Lowrey May 03 '22 at 06:25
  • They may not need to wait for all image resources to load. – Kaiido May 03 '22 at 06:26
  • @kaiido when you say "they don't risk facing this problem", do you mean the onload handler are only called after the body is parsed? Also, according to this [SO](https://stackoverflow.com/questions/50615101/what-does-async-defer-do-when-used-together?noredirect=1&lq=1), when both `async` and `defer` are used, `async` take precedence in modern browser, so `defer` has no effect here. – sgu May 03 '22 at 19:33
  • @sgu Dang! you're completely right, the defer is simply ignored, I always get this wrong because I don't understand why the heck they made it work like that. This will bite me someday... As for the attribute, no I mean that the HTML parser will attach the handler even before it set the src (or right after), so the parallel fetching will always end after the handler has been attached. – Kaiido May 04 '22 at 09:19
  • @Kaiido But the fact that "parallel fetching will always end after the handler has been attached" doesn't guarantee the correctness. The key question is whether the function `gapiLoad` is defined when the ` – sgu May 05 '22 at 19:28
  • @sgu Once again you're right... I don't know what I have with this question ;-) So indeed if the script is executed before the parser reached the callbacks definitions this will fail. – Kaiido May 06 '22 at 04:31
1

Onload EventHandler Supports these HTML tags: <body>, <frame> (deprecated), <iframe>, <img>, <input type="image">, <link>, <script>, <style>, <track>, <svg>, <use>, <foreignObject>, <image>, <object>, <embed. (Merci to @Kaiido for completing this list.)

If you want to be sure that your JS only runs when the DOM is loaded, you can load a handler function from the body tag.

Note You need to know when using onload with async that when scripts are loaded asynchronously, they delay the onload event. Often, asynchronously loaded scripts load other scripts.

function start() {
  gapiLoad();
  gisInit();
}

function gapiLoad() {
  console.log(1)
  // do something 
}

function gisInit() {
  console.log(2)
  // do something 
}
    
    // there are other stuffs...
<head>
<script async defer src="https://apis.google.com/js/api.js" ></script>
<script async defer src="https://accounts.google.com/gsi/client"></script>
</head>

<body onload="start()">
Maik Lowrey
  • 15,957
  • 6
  • 40
  • 79
  • 1
    `` is deprecated, `onload` also fires on ``, ``, ``, ``, ``, ``, `` and probabbly others I forget right now. But that's not the point of the question anyway. – Kaiido May 03 '22 at 06:26
  • @Kaiido Thanks for the hint. I will updated my answer on this point. – Maik Lowrey May 03 '22 at 06:32