0

I have a proxied object where I intercept get(). If the requested attribute does not exist then I load it from a script. That part works fine. But if a class is referenced then the instantiation goes ahead before the value gets loaded. The obvious answer is to use callbacks, but I can't find a way to intercept the construct() operation or some other means to hook in a callback to delay the instantiation process until the script load fully completes.

I do understand callbacks, fetch(), import() but the answers relating to any of those do not address the race condition when using Proxied objects.

The following has been tested with Chrome v71. I am not interested in compatability with other browsers, nor the use of jQuery or any other framework. I would have thought that ES6 functionality would suffice.

The module that I will attempt to load:

App.User = class {
    constructor(name) {
        this._name = name;
    }
    show() {
        console.log(`Hello, I am ${this._name}.`);
    }
}   

The web page and script causing the problem:

<!DOCTYPE HTML>
<html>
<head>
<title>Dynamic Script Loading</title>
</head>
<body>
<p> A test for dynamic script loading</p>
<script>

App = {};

function loadScript(url) {
  console.log(`loadScript: loading ${url}`);
  return new Promise(function(resolve, reject) {
    let script = document.createElement('script');
    script.type = 'text/javascript';
    script.async = false;
    script.src = url;
    script.onload = () => {
        console.log(`loadScript: loaded ${url}`);
        resolve(script);
    }
    script.onerror = () => reject(new Error("Script load error: " + url));
    document.body.appendChild(script);
    console.log(`loadScript: loading ${url}`);
  });
}

var hdlApp = {
    get: function(obj, prop) {
        if (prop in obj) {
            console.log(`App has property ${prop}`);
            return obj[prop];
        } else {
            console.log(`App does NOT have property ${prop}`);
            loadScript(`${prop}.js`).then(
                val => {
                    console.log('THEN');
                    return obj[prop]
                },
                err => {
                    console.log('CATCH');
                    return undefined;
                }
            );
        }
    }
}
window.App = new Proxy({}, hdlApp);

function test() {
    console.log('First test');
    u1 = new App.User('Fred');
    u1.show();
}
</script>
</body>
</html>

On the first test, the new App.User gets executed before the script has fully loaded. On a second attempt the script has fully loaded & compiled so it works. Which proves that the Proxy get() part is fine. I just don't know how to delay the constructor invocation until the script is fully loaded.

Running with DevTools console yields the following:

CyberFonic
  • 3,733
  • 1
  • 19
  • 21
  • 1
    It will never work this way. – Jonas Wilms Dec 28 '18 at 12:34
  • None of the claimed duplicate answers relate to the above question. Could you please remove the duplicate annotation. – CyberFonic Dec 29 '18 at 05:43
  • Have edited the question. If the example worked I wouldn't be asking the question. I think the problem is specific to needing to intercept the instantiation process and I can't see how that can be done with a callback. – CyberFonic Dec 29 '18 at 05:45
  • Your `App.User` evaluates to `undefined` because your `else` branch does not return anything. Returning `obj[prop]` is an entirely unrelated return within the nested function. You could ditch the Promise entirely, but either way sync XHR is an extremely bad idea and means this design approach is too. – loganfsmyth Dec 30 '18 at 02:18
  • If you really want dynamic module loading, look into existing systems like AMD modules. – loganfsmyth Dec 30 '18 at 02:21

0 Answers0