Асинхронный скрипт в DOM

Иногда модуль надо добавить в ДОМ и при этом этот модуль ничего не возвращает, а что-то делает и прописывается в глобале. Соотвественно, нужно как-то дождаться не только подключения самого скрипта, но и дождаться когда он отработает. Вот тут и приходит асинхронщина и обсёрверы. Рассказал как это правильно реализовать.

/* 
  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());
Tags
Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time)

Written by Fedor

© 2023