0

This code (modified from original to remove proprietary code) is used to initialize an analytics tracker. It is suggested to be initialized as shown below:

const app = window[NAMESPACE];  
if (!app.initialized) {
    const script = document.createElement('script');
    script.async = true;
    script.src = `${script_url}?namespace=${NAMESPACE}`;
    document.getElementsByTagName('script')[0].appendChild(script);
    app.initialized = true;
}

Assume that this simply retrieves a bunch of functions into that namespace, including the function foo().

We later call foo()...

if (app.initialized) {
    window[NAMESPACE].foo(....)
}

It seems to work, but I'm skeptical -- can foo() end up being called before the script is finished being retrieved? Before foo() even exists? And therefore app.initialized is actually somewhat meaningless?

edit: talked to the team suggesting it. Yes, it would possibly be a race problem. But the code I omitted before this section (because I thought it was irrelevant) sets up function stubs in the namespace that queue any function calls (i.e., adds them to an array) that are later executed after the script is loaded.

rrauenza
  • 6,285
  • 4
  • 32
  • 57
  • The code you did provide seems to handle the asynchronous nature of script loading correctly. It initializes the tracker by dynamically loading the script and waits until the script is loaded before invoking the foo function. However, without more context, information or the actual implementation of the foo function, it's hard to answer your question. – AztecCodes Jun 15 '23 at 19:29
  • 1
    @AztecCodes Isn't `script.async = true` a problem? – Barmar Jun 15 '23 at 19:33
  • I've clarified a bit in an edit. Doesn't matter what foo() does -- what matters is can I assume it exists yet... – rrauenza Jun 15 '23 at 19:34
  • 1
    You likely need to listen to the script's onload to set `app.initialized = true;`. See https://stackoverflow.com/questions/16230886/trying-to-fire-the-onload-event-on-script-tag – Ruan Mendes Jun 15 '23 at 19:37
  • 1
    What is this line `document.getElementsByTagName('script')[0].appendChild(script);` supposed to do exactly? I don't understand your logic... you want to **appendChild** `script` to an already existing ` – Roko C. Buljan Jun 15 '23 at 19:40
  • @rrauenza You need to think about a new solution, your current code does indeed lead to problems that can't 100% be fixed. – AztecCodes Jun 15 '23 at 19:41
  • @RokoC.Buljan You have to append the script tag to the DOM to make it load. It doesn't really matter where, so appending it to an existing script tag is as good as anything else. You know that tag exists, since you're running JS. – Barmar Jun 15 '23 at 19:43
  • 1
    @Barmar Not sure to understand you. I would rather expect a `elBodyOrHead.append(newScript)` or `existingScript.after(newScript)`... since it's not valid to have a ` – Roko C. Buljan Jun 15 '23 at 19:47
  • That's true when parsing HTML. It probably doesn't make a difference when you're adding to the DOM dynamically. – Barmar Jun 15 '23 at 19:55
  • @RokoC.Buljan Going back to the original source that is "suggested" to make sure I didn't modify it ... nope, that is what is suggested. I'm gonna have to talk to the authors, apparently – rrauenza Jun 15 '23 at 21:54
  • `foo` cannot execute if it does not exist at the time its target location is parsed. `script` will interrupt parsing to execute so it is a theoretical crap shoot. Where `script` is in the HTML may matter. [this blog page](https://flaviocopes.com/javascript-async-defer/) details `async` vs `defer` vs neither and says *"The best thing to do to speed up your page loading when using scripts is to put them in the head, and add a defer attribute to your script tag"* – radarbob Jun 16 '23 at 00:54

1 Answers1

0

Yes, it's a race problem.

But the authors actually work around it in their suggested load code (unintentionally only partially shown above) by manipulating window[NAMESPACE] with stubs that add any call arguments to an array which are later processed by the loaded script.

In other words, once the script is loaded, part of its execution looks at this array setup in window[NAMESPACE] and processes the "delayed" calls. Presumably it also overwrites the stubs once executed.

rrauenza
  • 6,285
  • 4
  • 32
  • 57