Иногда модуль надо добавить в ДОМ и при этом этот модуль ничего не возвращает, а что-то делает и прописывается в глобале. Соотвественно, нужно как-то дождаться не только подключения самого скрипта, но и дождаться когда он отработает. Вот тут и приходит асинхронщина и обсёрверы. Рассказал как это правильно реализовать.
/*
I want to use some module which adds some variable to global
but the module could be missing becouse sometimes I don't need it.
This must be done in async way and with some kind of observer
*/
const injectMessangerOnPage = () => new Promise((resolve, reject) => {
/*
the simplest way is to conditionally add <script> to the page
(webpach do the same for dynamic modules btw)
but what if insted of returning value,
module just do some hevylifting and then adds himself to global?
*/
const messengerSrc = 'myCDN/messenger.js';
const messengerScriptElement = document.createElement('script');
messengerScriptElement.setAttribute('src', messengerSrc);
document.body.appendChild(messengerScriptElement);
messengerScriptElement.onerror = (errorMsg) => reject(errorMsg);
/*
with Object.defineProperty I can "subscribe" to updates
and resolve promise only after my module adds himself to global
*/
Object.defineProperty(window, 'Messenger', {
set(value) {
this._value = value;
resolve();
},
get() {
return this._value;
},
configurable: true,
enumerable: true,
});
});
let _messenger = null;
/*
Here I add module to page if its not presented
and afterwards I must wait for his job to be dome
and he will be added to global.
*/
const messenger = () => new Promise((resolve) => {
if (_messenger) {
resolve(_messenger);
}
const { Messenger } = window;
if (!Messenger) {
injectMessangerOnPage()
.then(() => {
_messenger = new window.Messenger();
resolve(_messenger);
})
.catch((error) => {
throw new Error(error);
});
} else {
_messenger = new Messenger();
resolve(_messenger);
}
});
/*
Now I can call my function an it will return
promise with my dynamicly added module incide
*/
messenger().then((messenger) => messenger.connect());