I need a lazy loading variables in JavaScript. Taking inspiration from Any way to define getters for lazy variables in Javascript arrays? and from Self-references in object literal declarations I have tried this solution
var console = {
log: function(s) {
s = s.replace(/\\n/, '<br/>');
document.getElementById("console").innerHTML += s + "<br/>"
}
}
function MyClass() {
this.LazyProperty = {
_loaded: false,
_autoload: function() {
console.log("MyClassLazyProperty-1 " + JSON.stringify(this));
if (this._loaded) {
console.log("MyClass.LazyProperty-2 " + JSON.stringify(this));
return this;
} else {
setTimeout(function(self) {
self._loaded = true;
self.profile = {
displayName: "Loreto Parisi"
};
console.log("MyClass.LazyProperty-3 " + JSON.stringify(self));
self._autoload();
}, 300, this);
}
}
}._autoload()
this.User = {
username: "loretoparisi",
_autoload: function() {
console.log("MyClass.User-1" + JSON.stringify(this));
return this;
}
}._autoload()
}
var c = new MyClass();
console.log("c.User\n" + JSON.stringify(c.User));
console.log("c.LazyProperty\n" + JSON.stringify(c.LazyProperty));
<div id="console" />
Code Explanation
Supposed that I want to populate the lazy property by result of a asynchronous lookup (like a rest request), I need to keep a status of the loading at first lookup. The _autoload
function makes the async lookup and sets the response properties, so I would expect to have the object filled here:
c.LazyProperty
while the result is undefined
. Why?
EDIT
I have found out that something happens when calling the setTimeout
, so that the reference to this
is lost, so I have added a defer
function that shall keep it:
_defer : function() { this._autoload(); return this },
At that point the lazy loader will work properly:
var console = {
log: function(s) {
s = s.replace(/\\n/, '<br/>');
document.getElementById("console").innerHTML += s + "<br/>"
}
}
function MyClass() {
this.LazyProperty = {
_loaded: false,
_defer : function() { if(!this._loaded) this._autoload(); return this },
_autoload: function() {
console.log("MyClassLazyProperty-1 " + JSON.stringify(this));
setTimeout(function(self) {
self._loaded = true;
self.profile = {
displayName: "Loreto Parisi"
};
console.log("MyClass.LazyProperty-3 " + JSON.stringify(self));
}, 300, this);
}
}. _defer()
this.User = {
username: "loretoparisi",
_autoload: function() {
console.log("MyClass.User-1" + JSON.stringify(this));
return this;
}
}._autoload()
}
var c = new MyClass();
console.log("c.User\n" + JSON.stringify(c.User));
console.log("BEFORE: c.LazyProperty\n" + JSON.stringify(c.LazyProperty));
setTimeout( function() {
console.log("AFTER: c.LazyProperty\n" + JSON.stringify(c.LazyProperty));
},1000);
<div id="console" />
Given this solution, in the first execution I don't get why the property is undefined
after it has been assigned.