Reputation: 9484
I have a simple Javascript method (member function of an object) which keeps an internal cache (to avoid unnecessary ajax requests.)
I must be confused about Javascript scope rules or something because updates to the cache don't seem to be visible if they're made from within a nested function.
For example:
window.mynamespace = window.mynamespace || { };
window.mynamespace.foo = function()
{
this.cache = this.cache || { };
alert(JSON.stringify(this.cache));
this.cache["foo"] = "bar";
}
Here's a simple example that is meant to demonstrate that items inserted into the cache are being remembered across calls to mynamespace.foo()
.
In my onload
or $(document).ready()
handler, if I say:
window.mynamespace.foo();
window.mynamespace.foo();
I get two alerts, where the first shows me {}
and the second shows me {"foo" : "bar"}
. Okay great, that's exactly what I expected. So my cache seems to work.
But... if I try to update the cache from within a nested function (specifically within a callback handler of some async operation), the updates to my cache don't seem to propagate.
For example:
window.mynamespace = window.mynamespace || { };
window.mynamespace.foo = function()
{
var instance = this;
this.cache = this.cache || { };
alert(JSON.stringify(this.cache));
window.setTimeout(
function()
{
instance.cache["foo"] = "bar";
},
0
);
}
Here, I update the cache from within a nested function (a callback handler for window.setTimeout
).
In this case, if I call window.mynamespace.foo()
twice from within my document onload
handler, I get two alerts, each showing me {}
- an empty object. So now my cache is not updating.
Okay, so something about my understanding of Javascript scoping rules here is broken. I realize that this
means something different from within a nested function, which is why I specifically use the instance
variable which always refers to the window.mynamespace.foo
object.
Please educate me about my error here.
Upvotes: 0
Views: 233
Reputation: 287990
Note this is not a problem of scoping or nested functions, because the following code works:
window.mynamespace = window.mynamespace || {
cache: {},
foo: function() {
alert(JSON.stringify(this.cache));
(function() {
window.mynamespace.cache["foo"] = "bar";
})();
}
};
window.mynamespace.foo();
window.mynamespace.foo();
The problem is that setTimeout
code runs in the future. So of course the second alert shows an empty object, because the cache has not been modified yet.
Therefore, if you really need to update the cache asynchronously, you can use callbacks:
window.mynamespace = window.mynamespace || {
cache: {},
foo: function(callback) {
alert(JSON.stringify(this.cache));
setTimeout(function() {
window.mynamespace.cache["foo"] = "bar";
if(typeof callback == 'function') callback();
}, 0);
}
};
window.mynamespace.foo(function(){
window.mynamespace.foo();
})();
Upvotes: 1
Reputation: 6322
A window.setTimeout
with a 0ms
wait time will clear the calling stack. What's happening is:
CLEARED CALL STACK STATE:
5) key gets set from first foo call setTimeout
6) key gets set from second foo call setTimeout
TOP FRAME OF CALL STACK:
1) foo gets called first
3) second foo call
FOO FRAME OF CALL STACK:
2) first call of foo instantiates cache and alerts empty object
4) second call of foo alerts empty object
Here is a jsfiddle that confirms this behavior:
http://jsfiddle.net/jhgeh4c5/1/
Upvotes: 2