Skip to content

Instantly share code, notes, and snippets.

@mankins
Last active December 7, 2020 18:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mankins/c8cdae72de5635123a2ae7db14119b97 to your computer and use it in GitHub Desktop.
Save mankins/c8cdae72de5635123a2ae7db14119b97 to your computer and use it in GitHub Desktop.
Today's Proposal: Empty Promises

What's the current best practice for executing a function included by adding a (potentially async, deferred) script tag on the page?

We used to have scripts load synchronously, so we could immediately start using the library the next line:

<head>
<script src="/my-library-2001.js"/>
<script>
 myLib.hello(); // works!
</script>
</head>

And then we discovered that onload may be a better way to do this:

<head>
 <script src="/my-library-2003.js" onload="myLib.hello()" type="text/javascript"></script>
</head>

But managing a bunch of functions each wanting to tap into the onload was cumbersome, especially if you didn't need to know if the script was loaded or not. Script loaders sort of solved this, but no longer seem relevant.

Google Analytics (and others?) came up with the asynchronous function queuing:

<script src="/my-library-2010.js" onload="myLib.hello()" async="true" defer="true" type="text/javascript"></script>
<script type="text/javascript">
var _qaq = _qaq || [];
_qaq.push(() => { console.log('loaded'); myLib.hello(); });
</script>

Today's proposal: The Empty Promise

It seems like we'd be able to do this another way in 2020

<script type="text/javascript">
    MyLib = window.MyLib || new Promise((resolve, reject) => { window._mylib_init = { resolve, reject };});
</script>
<script src="/mylib-2020.js" async="true" defer="true" type="text/javascript"></script>

Use the window.MyLib.then() pattern to make sure library loaded. Ideally the library passes an api to resolve that promise.

<script type="text/javascript">
    // inside app code
    MyLib.then((api) => {
      console.log('my library loaded.');
      // api.getProducts(); // or whatever
    });
 
</script>

Inside the library's init function:

  if (!window.MyLib) {
    // setup the empty promise pipeline
    window.MyLib = new Promise((resolve, reject) => {
      window._mylib_init = { resolve, reject };
    });
  }

  // resolve promise
  if (window._mylib_init) {
    window._mylib_init.resolve(MyLibApi);
    delete window._mylib_init; // clean up
  }

This seems to work: the calling pages can use a familiar Promise interface to wait for the library to load. Thoughts to a better way to do this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment