My custom blazor component uses an external script (hosted on a CDN). Usually one would reference it in index.html, but I don't want to do that - I want the component to be self contained and easy to use.
The typical solution is a script loader. A popular implementation has been floating around for years: here and here.
wwwroot/js/scriptLoader.js (referenced in index.html):
let _loadedScripts = [];      // list of loading / loaded scripts
export async function loadScript(src) {
  // only load script once
  if (_loadedScripts[src])
    return Promise.resolve();
  return new Promise(function(resolve, reject) {
    let tag  = document.createElement('script');
    tag.type = 'text/javascript';
    tag.src  = src;
    // mark script as loading/loaded
    _loadedScripts[src] = true;
    tag.onload = function() {
      resolve();
    }
    tag.onerror = function() {
      console.error('Failed to load script.');
      reject(src);
    }
    document.body.appendChild(tag);
  });
}
Then I create a component where I want to load a custom script.
FooComponent.razor:
@inject IJSRuntime _js
// etc...
protected override async Task OnAfterRenderAsync(bool firstRender)
{
  await base.OnAfterRenderAsync(firstRender);
  if (firstRender)
  {
    // load component's script (using script loader)
    await _js.InvokeVoidAsync("loadScript", "https://cdn.example.com/foo.min.js");
  }
  // invoke `doFoo()` from 'foo.min.js' script
  await _js.InvokeVoidAsync("doFoo", "hello world!");
}
That works. I can use the <FooComponent /> and it will load its own script file.
But if I use the component multiple times, I run into a race condition:
- instance 1 of the component
- tries to load the script
- script loader loads the script
- the component can use it
 
- instances 2+ of the component
- they are loading at the same time as instance 1!
- each tries to load the script, but the loader refuses to load it more than once
- they immediately try to use the script - but it's still busy loading!
- so they all fail and the app crashes (exceptions, etc.)
 
How can I refactor the code to ensure that the script is actually finished loading, before using it?
 
    