Trigger callback function when two event listeners are fulfilled

Viewed 98

I am looking for an elegant way to call a callback function when two load event listeners are fulfilled. Currently I am using a variable which has the state of the event listener and is checked in an interval. This is an example of my depicted situation:

function executeExtScript(cb, srcStyle, srcJs) {
    // 01
    let scripts = document.getElementsByTagName('script');
    let scriptExists = false;
    for (let i = scripts.length; i--;) {
        if (scripts[i].src === srcJs) scriptExists = true;
    }
    scriptExists === true ? cb() : loadScript();
    // 02
    function loadScript() {
        let loadFinish = [false, false];
        let head = document.head;
        let style = document.createElement("link");
        style.setAttribute("rel", "stylesheet");
        style.setAttribute("href", srcStyle);
        let script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = srcJs;
        script.onload = () => {loadFinish[0] = true}
        style.onload = () => {loadFinish[1] = true}
        var timer = setInterval(function() {
            if (loadFinish[0] && loadFinish[1]) {
                cb();
                clearInterval(timer);
            }
        }, 100);
        head.appendChild(style);
        head.appendChild(script);
    }
}

Is there any more elegant way to solve this problem - in particular I would like to prevent using a set interval function provided that they may run for a very long time if I fail to fetch one of the files

Thanks in advance

1 Answers

Create Promises from them both and use Promise.all.

const promisifyLoad = (element) => new Promise((resolve, reject) => {
    element.addEventListener('load', resolve);
    element.addEventListener('error', reject);
});

function executeExtScript(cb, srcStyle, srcJs) {
    const scriptExists = [...document.querySelectorAll('script')]
        .some(script => script.src === srcJs);
    if (scriptExists) cb();
    else loadScript();

    function loadScript() {
        const { head } = document;
        const style = head.appendChild(document.createElement('link'));
        style.setAttribute("rel", "stylesheet");
        style.setAttribute("href", srcStyle);
        const script = head.appendChild(document.createElement('script'));
        script.src = srcJs;
        Promise.all([
            promisifyLoad(style),
            promisifyLoad(script)
        ])
            .then(cb)
            // .catch(handleErrors);
    }
}
Related