Search code examples
javascriptecmascript-6es6-proxy

How to use Proxy to watch if a variable is (no longer) undefined


Please, forgive my n00bility... I am trying to use Proxy to execute some code after a global var stage is no longer undefined. Here's my naive attempt:

```

var stage = undefined
let myObj;
let st = {
    stage: stage,
}
let lr = {
    get: function(obj:any, prop:any):LightRay{
        myObj = new SomeObjThatNeedsStage(obj[prop]) // <= supposed to be non-undefined stage
        return myObj
    },
}
let p = new Proxy(st, lr)
// At this point I expect the lr handler to have been executed if the stage var has been instantiated by some external code that's out of my control.

```

Obviously, I misunderstand how the Proxy works, but I tried various scenarios, non of which works. Passing the undefined stage as a target to the proxy doesn't work (getting Cannot create proxy with a non-object as target or handler error). How can I use Proxy in this scenario? Why can't I observe the undefined stage with Proxy until it is instantiated, and then execute the callback?

FYI:

Stage var is global and I do not have control over it (it is instantiated by create.js at some point). All code is executed from a UMD module. I'm using typescript (transpiling down to es5). Object.defineProperty() is available in my browser. Also, the stage (of type createjs.Stage), when instantiated, does contain setter/getter of a different type, which makes it problematic when working with typescript.


Solution

  • Updated answer

    You've said you don't control how stage is set.

    Watching for the change is an anti-pattern and code maintenance nightmare. Look for any other solution you can, such as making the one module dependent on the other so you know it's loaded before your module runs.

    However, you've said there is no other way, so:

    You can't use Proxy for this, because in order for it to be effective, the code setting stage would have to set it via the proxy, not directly.

    You've said you don't control how stage is declared. That means you can't use Object.defineProperty for this, either, because the property on the global object that var creates has configurable: false.

    If there's absolutely, positively no way to get coordination between your module and whatver's doing stage, you'll have to resort to a timer loop. This is not a good idea, but may be your only real option:

    var stop = Date.now() + 30000;
    var timer = setInterval(function() {
        if (typeof stage !== "undefined" || Date.now() > stop) {
            clearInterval(timer);
            timer = 0;
            // Your code here, if `stage` is still undefined, you hit the timeout
        }
    }, 50); // 50ms interval or whatever you need
    

    Original answer

    By far the best solution here is: Don't do that. Instead, use an object with a setter:

    var stuff = {
        _stage: undefined,
        get stage() {
            return _stage;
        },
        set stage(value) {
            if (_stage === undefined && value !== undefined) {
                _stage = value;
                // Trigger your code here
            }
        }
    };
    

    Then have the other code doing this set stuff.stage rather than setting stage.