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>
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?