0

I'm creating a plugin system for a library. I'm getting the function from another file with ajax, wrapping and returning only the function with the Function constructor and then add the function to the window object and I'm doing this in a loop and at last when i'm checking the window object, it's empty. why?

Here's the code

x.html (Main File)

<html>
    <head>
        <script src="//cdn.jsdelivr.net/npm/eruda"></script><script>eruda.init();</script>
        <script>
            window.addEventListener('error', function(e) {
                console.error(e, e.message, e.filename, e.lineno, e.colno, e.error);
            });
            if (typeof window.example !== 'object') {
            window.example = {
                plugins: {}
            };
        }
        </script>
    </head>
    <body>
        Text
        <script type="module">
            import { loadPlugin } from "./y.js";
            await loadPlugin(['bold','italic']);
            console.log(window.example.plugins)
            console.log(window.example.plugins.italic)
        </script>
    </body>
</html> 

y.js (Importer)

export async function loadPlugin(plugins, namespace) {
    for (var i = 0; i < plugins.length; i++) {
        if (typeof window.example.plugins[plugins[i]] === 'undefined') {
            fetch(`z.js`)
            .then((response) => response.text())
            .then(function(code) {
                if (code.trim() !== '') {
                    try {
                        code += 'return theme;'
                        window.example.plugins[plugins[i]] = (new Function(code))();
                    } catch (err) {
                        console.log(err);
                    }
                }
            });
        }
        if (i === (plugins.length - 1)) {
            return new Promise(function(resolve) {
                resolve(window.example.plugins);
            });
        }
    }
}

z.html (The file to import function from)

function theme() {
    console.log('Hooray!');
}

I hope you all understood the problem yet, please help.

Thanks ,

  • Your `for` loop is synchronous. It **starts** the process of loading the functions, but doesn't wait for it to finish. The code checking the `window` object at the end is doing so **before** any of the asynchronous work has a chance to happen. (The fact you've wrapped it in a promise doesn't matter, that promise isn't in any way dependent on the previous promises being settled before it settles.) See the linked question's answers for what to do. (Basically: Build up an array of the promises, then wait for them all to settle via `Promise.all` before expecting `window` to be populated.) – T.J. Crowder Apr 29 '23 at 14:07

1 Answers1

0

The problem lies between your fetch's Promise is not being await instead of not being injected to the window correctly. One solution is to await your fetch so that it can be sure to be successfully loaded when loadPlugin's Promise is resolved.

export async function loadPlugin(plugins, namespace) {
  for (var i = 0; i < plugins.length; i++) {
    if (typeof window.example.plugins[plugins[i]] === 'undefined') {
      await fetch(`z.js`)
        .then((response) => response.text())
        .then(function (code) {
          if (code.trim() !== '') {
            try {
              code += 'return theme;';
              window.example.plugins[plugins[i]] = new Function(code)();
            } catch (err) {
              console.log(err);
            }
          }
        });
    }
    if (i === plugins.length - 1) {
      return new Promise(function (resolve) {
        resolve(window.example.plugins);
      });
    }
  }
}
AngYC
  • 3,051
  • 6
  • 20