Timer in a closure - javascript

Can anyone please tell my why this doesn't work?
(function() {
window.Test = {};
})();
Test.Timers = {
c: null,
startTimer: function() { c = 0; setTimeout(this.doWork, 0); },
doWork: function() {
c++;
alert(c);
setTimeout(this.doWork, 0);
}
};
When I call Test.Timers.startTimer(), it only alerts once with a 1.
Thanks

A method doesn't "remember" its owner (its this); you can copy a method from one object to another, and treat it like any other function. It only has the right owner when you actually call it using dot-notation, e.g. this.doWork().
So your problem is that you're passing the function this.doWork to setTimeout, and then it gets called as a function without knowing its owner, and suddenly its this is window instead of your timer object. To fix this, you need to keep track of your this yourself. For example, you might write:
Test.Timers = (function () {
var newTimer = {
c: null,
startTimer: function() {
this.c = 0;
setTimeout(function () { newTimer.doWork(); }, 0);
},
doWork: function() {
this.c++;
alert(this.c);
setTimeout(function () { newTimer.doWork(); }, 0);
}
};
return newTimer;
})();
or:
Test.Timers = (function () {
var startTimer = function() {
newTimer.c = 0;
setTimeout(doWork, 0);
};
var doWork = function() {
newTimer.c++;
alert(newTimer.c);
setTimeout(doWork, 0);
};
var newTimer = {
c: null,
startTimer: startTimer,
doWork: doWork
};
return newTimer;
})();
(Note that I also changed c to this.c or newTimer.c where necessary, since your version refers repeatedly to window.c. Also note that in the second version, if you don't need external code to be able to access c, you can change it to a local variable, making things cleaner.)

As per your comment to ruakh's answer, I prefer the following approach, myself:
Test.Timers = (function () {
var this_ = {
c: null,
startTimer: function() { this_.c = 0; setTimeout(this_.doWork, 0); },
doWork: function() {
this_.c++;
alert(this_.c);
setTimeout(this_.doWork, 0);
}
};
return this_;
})();
That way, the meaning is clear as this_ looks like this, and all you have to do is get used to the closure pattern of making an anonymous function and calling it right away. Also note that I fixed your reference to c to refer to this_.c instead of a global variable c.
Alternatively, you can use .bind() to bind the function's this to a particular thing. This is built-in to Chrome's V8, at least, and perhaps Firefox as well:
Test.Timers = {
c: null,
startTimer: function() { this.c = 0; setTimeout(this.doWork, 0); },
doWork: function() {
this.c++;
alert(this.c);
setTimeout(this.doWork, 0);
}
};
Test.Timers.startTimer = Test.Timers.startTimer.bind(Test.Timers);
Test.Timers.doWork = Test.Timers.doWork.bind(Test.Timers);

Related

What's the reason of the following JavaScript syntax when using closures?

Studying about closures, I looked at the Developer's Mozilla article about it and saw the code below:
var counter = (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
};
})();
console.log(counter.value()); // 0.
counter.increment();
counter.increment();
console.log(counter.value()); // 2.
counter.decrement();
console.log(counter.value()); // 1.
I'm confused about how the function is attributed to the variable counter, because the function is initially envolved by those parentheses, and after all, there are also two unreaseble parentheses together... I just wondered, what's the reason of that syntax? I certainly would do:
var counter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
};
};
But then I got the error
Uncaught TypeError: counter.value is not a function
When I do console.log(counter.value());
Can someone please explain it to me?
What you're looking at is an Immediately Invoked Function Expression or IIFE. This code is creating a function, then immediately calling that function, then assigning the return value of the function to counter. So counter isn't a function, it's an object with three properties: increment, decrement, and value.
The reason that they used an IIFE was to make what's essentially a private variable. privateCounter is only in scope to other code inside that function, which means only increment, decrement, and value can access it.
If they didn't care about making the variable private, the equivalent code would be:
var publicCounter = 0;
function changeBy(val) {
publicCounter += val;
}
var counter = {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return publicCounter;
}
}

Can a javascript function run in setTimeout callback only?

This is the question:
Define a function named print which just print out the parameters it gets.
But it will not print out anything if it's called normally.
Only in a setTimeout callback will become effective.
e.g:
setTimeout(function() {
print('123'); //===> 123
});
print('456'); //===> nothing output
I have one solution but I don't think it's a good way, I rewrite the setTimeout.
I want a better solution curiously.
var print = function() {
'use strict';
var __origSetTimeout = window.setTimeout;
window.setTimeout = function(fn, delay) {
var _fn = new Function(`(${fn.toString().replace(/print\(/g, 'print.call(this,')}).call(this);`);
return __origSetTimeout.call(window, _fn.bind({
isFromSetTimeout: true
}), delay);
};
return function print(word) {
if (!this || !!this && !this.isFromSetTimeout) return;
console.log(word);
};
}.call(null);
You can use scope to solve this, for example
function A(){
let print = function(str){
console.log(str);
}
this.setTimeout = function(){
setTimeout(function(){
print('123');
}, 1000);
}
}
let a = new A();
a.setTimeout();
You could use a monkey patch for an extension of the print function with an additional check for a this object and a property for printing.
// simple function with output
function print(s) {
console.log(s);
}
// apply monkey patch
void function () {
var p = print;
print = function () {
if (this && this.timeout) {
p.apply(this, arguments);
}
}
}();
// bind additional information
setTimeout(print.bind({ timeout: true }, '123'));
print('456');

Cancelling runaway JavaScript timeouts/intervals within a revealing module pattern

I have a fairly standardised revealing module pattern to create objects for use as instances. Sometimes, within these patterns there are timeouts or intervals that need to be cancelled in the case that the module is no longer being used or referenced within external code.
Simplified example of this pattern:
function test() {
window.timer = maketimer();
}
function maketimer() {
var cls, my;
my = {
increment: 0,
timerid: null,
exec_timer: function() {
my.timerid = window.setInterval(my.time, 2000);
},
time: function() {
console.log("timer: ", my.timerid, my.increment++);
}
},
cls = {
//...
}
my.exec_timer();
return cls;
};
test();
// some time later...
test();
In the case that test is called twice, for whatever reason, the variable window.timer is replaced with a second instance of maketimer but the first instance timer continues to run.
A lot of the time, my modules are intrinsically linked to DOM nodes, and more often than not the DOM nodes are removed with the old instances, so I could in theory check for the non-existence of the node or its placement outside of the DOM, and then cancel the interval in this case.
This is far more generic however, and I would like to be able to deal with timeouts outside of the DOM environment.
In this case I would wrap the whole function in an IIFE that contains an instance variable. In it, you save the timer. And every time a new one is started, the old one is destroyed:
(function(window) {
var timerInstance = null;
window.maketimer = function() {
var cls, my;
if(timerInstance) {
timerInstance.destroyInstance();
}
my = {
increment: 0,
timerid: null,
exec_timer: function() {
my.timerid = window.setInterval(my.time, 2000);
},
time: function() {
console.log("timer: ", my.timerid, my.increment++);
},
destroyInstance: function() {
window.clearInterval(my.timerid);
}
},
cls = {
//...
}
my.exec_timer();
timerInstance = my;
return cls;
}
})(window);
function test() {
window.timer = maketimer();
}
test();
test();
Just out of curiosity, why do you need to have the instance on a global variable? window.timer is pretty generic and could be overridden by other scripts.
Try it: Update your code below..
var settimer;
function test() {
clearTimeout(settimer);
settimer= setTimeout(function () {
window.timer = maketimer();
}, 100);
}
Expanding on the answer given by #nils, I've created the below example for constructing a module which takes in a single DOM node and will only clear the previous instance/timer if the DOM node has already been used:
<div id="element1">[code here that may be updated with XHR]</div>
<div id="element2">[code here that may be updated with XHR]</div>
(function(window) {
var timerInstances = [];
window.maketimer = function(element) {
var cls, my, a;
// find instances where the passed element matches
for (a = 0; a < timerInstances.length; a += 1) {
if (timerInstances[a].element === element) {
console.log("instance already exists for element", element, "destroying...");
timerInstances[a].in.destroyInstance();
}
}
my = {
increment: 0,
timerid: null,
exec_timer: function() {
my.timerid = window.setInterval(my.time, 2000);
},
time: function() {
console.log("timer: ", my.timerid, my.increment++);
},
destroyInstance: function() {
window.clearInterval(my.timerid);
}
},
cls = {
//...
}
my.exec_timer();
timerInstances.push({
'element': element,
'in': my
});
return cls;
}
})(window);
function test(element) {
window.timer = maketimer(element);
}
test(document.getElementById("element1")); // produces 1 timer
test(document.getElementById("element1")); // cancels last, produces 1 timer
test(document.getElementById("element2")); // produces 1 timer
The identifying argument could be anything here - in this case it's a DOM node, but it could easily be a Number, String etc.
The accepted answer is very helpful and is still preferred if you wish to maintain the rule of having one instance at a time on the document.

Invoking jQuery function every x second (object literal pattern)

I'm trying to give my jQuery a bit more a structure with a basic object literal pattern and encountered an issue with calling my custom move function as per the following.
(function() {
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
}, 200);
});
}
};
})();
window.setInterval(function(){
movingMenu.move();
}, 1000);
I'm trying to call this function every second but the calling part itself doesn't seem to work. I suspect that the variable movingMenu is maybe outside the scope that within window.setInterval it doesn't have a clue in which object this function belongs to?
Demo is available JSFiddle
The code you post here would not work, as you use an IIFE to wrap the object, the setInterval can't access movingMenu. However, your code in jsfiddle is correct. You can either dewrap the IIFE, or put the setInterval into the IIFE, or return a object that exposed the move function. You just need to ensure that movingMenu, or the move is accessible to setInterval.
Use this to get the ref of that menu in your function, as its an attribute of movingMenu, not an variable.
Altered jsfiddle
Move everything out of IIFE:
var movingMenu = {
menu: $('.nav').children('li'),
move: function () {
// VVVV you need to use this to reference to `movingMenu`, so this.menu is the referenced `li`s.
this.menu.each(function () {
$(this).animate({
top: '+=50'
}, 200);
});
}
};
window.setInterval(function () {
movingMenu.move();
}, 1000);
Move setInterval into IIFE as well:
(function(){
var movingMenu = {
menu: $('.nav').children('li'),
move: function () {
// VVVV you need to use this to reference to `movingMenu`, so this.menu is the referenced `li`s.
this.menu.each(function () {
$(this).animate({
top: '+=50'
}, 200);
});
}
};
window.setInterval(function () {
movingMenu.move();
}, 1000);
})();
Yes, you are right, the variable movingMenu is out of scope.
Also, to use the property menu inside the method you need to use this.menu. There is no object scope in JavaScript, so even if you are "inside" the object, you can't directly access the object properties.
(function() {
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
// use "this" to access properties
this.menu.each(function() {
$(this).animate({
top: '+=50'
}, 200);
});
}
};
// use the variable inside the scope
window.setInterval(function(){
movingMenu.move();
}, 1000);
})();
You're correct about movingMenu being unavailable. To get around this, you want to set your module to a variable and return whatever you want to access outside of the module.
var movingMenu = (function() {
return {
menu: $('.nav').children('li'),
move: function() {
this.menu.each(function() {
$(this).animate({
top: '+=50'
}, 200);
});
}
};
})();
window.setInterval(function(){
movingMenu.move();
}, 1000);
edit: whoops, I read object literal pattern then saw the module pattern and ran in the wrong direction.
The scope of the movingMenu object is limited to the anonymous function that wraps it... function() { var movingMenu = { ... } }.
You're using a syntax that is allowing you to define that anonymous function, and then invoke or call it immediately.
Declaration: (function() { var movingMenu = {..} })
Invocation: (function() { ... })()
This would be the same as if you said...
var foo = function() { var movingMenu = {...} };
foo();
So, in that context, movingMenu is a variable that is defined inside another function, foo. Nothing oustide of foo knows anything about movingMenu. This is the idea of scope. movingMenu exists in the scope of foo.
So to get the functionality of movingMenu outside the scope of foo, we can return movingMenu from foo. What this does is makes the return value of foo the movingMenu object, like so...
var foo = function() {
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
},200);
});
}
};
return movingMenu;
};
var menuHandler = foo(); // save movingMenu, returned from foo()
Then, you can use menuHandler like you would movingMenu.
window.setInterval(function () {
menuHandler.move();
}, 1000);
Since you're declaring the function anonymously (not giving it a name, like I did with foo), and then you're invoking it right away, you want to store the return value right away, since you won't be able to invoke that method again.
var foo = function() {
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
},200);
});
}
};
return movingMenu;
};
var menuHandler = foo(); // save movingMenu, returned from foo()
Then, you can use menuHandler like you were trying to use movingMenu. Really, could use the name movingMenu instead of menuHandler, I just think it's less confusing this way.
window.setInterval(function () {
menuHandler.move();
}, 1000);
So putting it altogether, with the anonymous function...
var menuHandler = (function() {
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
},200);
});
}
};
return movingMenu;
})();
window.setInterval(function () {
menuHandler.move();
}, 1000);
So why would you do this? Why not just make movingMenu public and invoke it directly, instead of wrapping it in an anonymous method and exposing it as a return value from that method? The reason again has to do with scope. By limiting the scope and controlling what is exposed, you can actually create (somewhat, details are beyond the scope of this question) private properties in js.
For example...
var menuHandler = (function() {
// ADDED NEXT LINE FOR EXAMPLE:
var privateCounter = 0;
var movingMenu = {
menu: $('.nav').children('li'),
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
},200);
// ADDED NEXT LINE FOR EXAMPLE:
console.log("Count is now: " + (privateCounter++).toString());
// look at the console tab of browser's dev tools (F-12 on windows)
});
}
};
return movingMenu;
})();
From this example, you can now see that movingMenu is exposed (as menuHandler), and it has the ability to use the private variable privateCounter, however, privateCounter is not exposed. So basically this pattern makes everything private initially, so you can expose just what you want to be public.
var menuHandler = (function() {
var privateCounter = 0,
menu = (function() {
return $('.nav').children('li');
})();
var movingMenu = {
move: function() {
menu.each(function() {
$(this).animate({
top: '+=50'
}, 200);
});
console.log("Count is now: " + (privateCounter++).toString());
}
};
return movingMenu;
})();
setInterval(function() {
menuHandler.move();
}, 1000);
.nav {
position: absolute;
}
.nav li {
position: relative;
top: 0;
}
ul {
list-style: none;
}
ul li {
display: inline-block;
background: red;
border-radius: 50%;
color: white;
width: 50px;
height: 50px;
text-align: center;
line-height: 3;
}
<ul class="nav">
<li>Look</li>
<li>Play</li>
<li>Eat</li>
<li>See</li>
</ul>
NOTE In my snippet I've modified your code to make the menu property non-static. This will handle items being added or removed from the menu.

Passing local functions to setTimeout()

I have written the following function.
function obj()
{
this.a;
}
obj.prototype.catch = function()
{
alert('Catched')
}
obj.prototype.do = function()
{
alert('called');
}
What i need is, to call obj::catch() after obj::do() is called and the call must be performed from inside obj::do()
So how to pass the local function of obj to setTimeout
i have tried
obj.prototype.do = function()
{
window.setTimeout('"'+this.catch+'()"',1000);
alert('called');
}
It does not worked
Then i tried
obj.prototype.do = function()
{
window.setTimeout('"'+this+'.catch()"',1000);
alert('called');
}
which gave me the following error on Chrome console
Uncaught SyntaxError: Unexpected token ILLEGAL
So i tried the following dirty method(is it really dirty ?)
obj.prototype.do = function()
{
this.pid = randomVal(100);
window['temp'+this.pid] = this;
window.setTimeout("temp"+this.pid+".catch();",1000);
alert('called');
}
function randomVal(bound)//returns a random number between 0 and <bound>
{
return (Math.floor(Math.random()*(bound)));
}
That worked.
so why the first two methods not worked.Is there any other way to do the same thing without global variables..
The second method and last method are almost similar .But why am i gettng the error in second method..?
The worked code can be found here
http://jsfiddle.net/jXhAs/
Don't pass strings to setTimeout … ever.
var self = this; // Because the scope will change
setTimeout(function () { self.catch() },1000);
Or if you are using JS 1.8.5:
setTimeout(this.catch.bind(this),1000);
You can read more about bind
You should pass a function to setTimeout (not a string):
Example:
var self = this;
setTimeout(function(){
self.catch();
},1000);
use a closure
obj.prototype.do = function()
{
window.setTimeout((function(that){
return function(){
that.catch();
};
})(this),1000);
alert('called');
}
Why go through all of this effort, just pass the function.
function obj() {
this.a;
}
obj.prototype.
catch = function() {
alert('Catched')
}
obj.prototype.do = function() {
setTimeout(this.
catch, 1000);
}
var test = new obj();
test.do();​

Categories