setInterval with (this) - javascript

Please, someone can explain me what is the meaning of (this) at the end of the function in a setInterval :
function Klass(name) {
this.name = name;
this.handle = null;
this.startTimer = function() {
this.handle = setInterval(function(obj) {
return(function() {
alert(obj.name);
});
}(this), 5000); // <-------------------- (this)
}

The use of this in the construct is intended to preserve the meaning of this at the point setInterval is called for the actual call back that is executed at the given interval. Without the manual preservation this would become the owner of the function at the point setInterval was called.
Here's a very nice article on this subject
http://www.quirksmode.org/js/this.html
Another way this could be done which may be a bit clearer is the following
var self = this
this.handle = setInterval(function() { alert(self.Name); }, 5000);

Related

Calling class methods inside javascript class

This is a Vue class. The method signOut() should fire when the timer ticks. The timer works, except the call signOut().
The problem is with accessing the class method. I'm confused with this, self and access modifiers.
I tried with this.signOut() but it does not work.
How can I call the method signOut?
"use strict";
(async (globals, config, loader, application) => {
const storageLocal = await loader.services.storage.local.getAsync();
class HeaderComponent {
#foo = a;
constructor(tag) {
this.tag = tag;
this.timer();
}
signOut() {
storageLocal.delete('account');
window.location = '/signin.html';
}
timer() {
//document.getElementById("timer"),
var counter = -1;
var timeout;
var startTimer = function timer() {
counter++;
console.log(counter);
signOut(); //<- error can't call class method
timeout = setTimeout(timer, 10000);
};
function resetTimer() {
// here you reset the timer...
clearTimeout(timeout);
counter = -1;
startTimer();
//... and also you could start again some other action
}
document.addEventListener("mousemove", resetTimer);
document.addEventListener("keypress", resetTimer);
startTimer();
}
data() {
return { account: storageLocal.account };
}
}
const component = new HeaderComponent('component-header')
loader.components.set(component.tag, component);
})(window, window.config, window.loader, window.application);
Please note:
signOut() {
storageLocal.delete('account');
window.location = '/signin.html';
}
timer() {
//document.getElementById("timer"),
var counter = -1;
var timeout;
var startTimer = function timer() {
as you can see 'signOut()' is 2 levels below active functions. The logic says it would work like this.parent.signOut() but it DOES NOT !
EDIT3: this.signOut(); will produce
Uncaught TypeError: Cannot read property 'signOut' of undefined
at timer (header.js:30)
at HTMLDocument.resetTimer
The function creates a new context. You need to switch to arrow function and use this.signOut(). Simplified example:
timer() {
var counter = -1;
var timeout;
var startTimer = () => {
counter++;
console.log(counter);
this.signOut();
timeout = setTimeout(startTimer, 1000);
};
setTimeout(startTimer, 1000);
}
Moreover, you have two signOut() methods defined in one class.
You need this and call it like this.signOut()
The startTimer-function does not run in the context of the HeaderComponent's instance.
this in startTimer will point to window when it's executed as a handler in setTimeout.
In order to access the the instance of HeaderComponent, either use an arrow function (as pointed out in an earlier answer. See also Arrow function expressions) which will point this to the outer context (which is HeaderComponent's instance) or define an identifier in timer which points to the instance (eg. const self = this;) and use self instead of this in startTimer.
To apply this to your example (for the sake of consistency, I used var instead of const):
timer() {
var counter = -1;
var timeout;
var self = this;
var startTimer = function() { // Don't use a named function here, it only leads to more confusion
counter++;
console.log(counter);
self.signOut(); // Use `this` of the outer context
timeout = setTimeout(startTimer, 10000); // Use the declared identifier
};
// Rest of the method
}
this is Javascript may be a bit confusing to those who come from different programming languages. If you want to get into more detail, I recommend reading into the MDN reference for this and into Closures

Method called from setInterval, stops executing halfway through

My draw() function stops executing after calling this.circle(). If I call the draw() method directly, it works perfectly. However, if I use setInterval(this.draw, 1000), it seems to return right when calling this.circle(). circle() is never executed either. Am I misusing setInterval?
function Ball() {
this.start = function() {
return setInterval(this.draw, 1000);
}
this.circle = function() {
console.log('1');
}
this.draw = function() {
console.log('2');
this.circle();
console.log('3');
}
this.circle() is never executed, and console.long('3') (or anything after it) is never reached.
The result is console.long('2') being repeatedly printed every 1 second
When you use setInterval, this will refer to window. window doesn't have a function called draw, so it will throw an error and stop execution. To fix it, you need to bind the value of this.
return setInterval(this.draw.bind(this), 1000);
“this” is no longer what you think it is, and you have lost context to it, it no longer represents Ball like you’d like it to. You can bind your draw function like “this.draw.bind(this)” or you can capture “this” using a line like “const _this = this;” and then only reference “_this” in your functions to make sure you are always accessing the correct one.
Some reading on the closures
https://javascript.info/closure
As stated by #iagowp this will refer to the window when your method is called by setInterval. Instead, you can fix this by using an ES6 arrow function to call this.draw(). This way your this is referring to the Ball object rather than the function which called this.draw().
See working example below:
function Ball() {
this.start = function() {
return setInterval(_ => this.draw(), 1000);
}
this.circle = function() {
console.log('1');
}
this.draw = function() {
console.log('2');
this.circle();
console.log('3');
}
}
let b = new Ball();
b.start();

"this" within setTimeout self executing function is undefined

I know that this within setTimeout will correspond by default to window, so I have been using bind and passing it through to the self executing function, but when setTimeout finally runs (it's fine on the first run, called by TeaBot._manageTeaRound(), but it's not fine when self executing) this is undefined. Here's my code, where am I going wrong? (I have deleted some lines of code which might not be relevant). Thanks for your help :)
TeaBot.prototype._manageTeaRound = function(originalMessage, channel){
var self = this;
self.teaMaker = this._getUserById(originalMessage.user);
//now wait 3 minutes for people to send their order
self._runTimer(self,channel);
}
TeaBot.prototype._runTimer =function(self, channel) {
// do stuff
console.log(self.teaMaker.name); //undefined
var interval = self.interval,
teaMaker = self.teaMaker;
console.log("self.interval is " + self.interval);
if(interval === 0){
interval++;
self.interval = interval;
setTimeout(self._runTimer.bind(self, channel), 180000);
}else{
self.interval = 0;
}
}
This line is problematic:
setTimeout(self._runTimer.bind(self, channel), 180000);
Function TeaBot.prototype._runTimer expects self to be the first param - Function.prototype.bind() first param is context (function's this). Try use it like this:
setTimeout(self._runTimer.bind(self, self, channel), 180000);
Or leave context empty, becouse you are not using this at all:
setTimeout(self._runTimer.bind(undefined, self, channel), 180000);
This is because you are not using the value of this inside of _runTimer. You are binding a value this, but _runTimer couldn't care less.
_runTimer cares about its two parameters. The 1st being the context (self). The way your code is structured, there's no reason to use .bind here.
setTimeout(function(){
self._runTimer(self, channel);
}, 180000);
(Since you are passing the context (self) to the function, it makes no sense to have _runTimer be a part of TeaBot.prototype, since it needs a context passed to it.)
As an alternative, you can drop the self parameter, and have _runTimer reference this. That's when you would need to use .bind().
TeaBot.prototype._manageTeaRound = function(originalMessage, channel){
this.teaMaker = this._getUserById(originalMessage.user);
//now wait 3 minutes for people to send their order
this._runTimer(channel);
};
TeaBot.prototype._runTimer = function(channel) {
// do stuff
console.log(this.teaMaker.name);
var interval = this.interval,
teaMaker = this.teaMaker;
console.log("this.interval is " + this.interval);
if(interval === 0){
interval++;
this.interval = interval;
setTimeout(this._runTimer.bind(this, channel), 180000);
} else{
this.interval = 0;
}
};

How to kick-ass pass scope through "setInterval"

I'm currently wondering if there is a better solution than passing this scope to the lambda-function via the parameter 'e' and then passing it to 'funkyFunction' using call()-method
setInterval(function(e){e.funkyFunction.call(e)}, speed, this)
(Minor question aside: I'd been reading something about memory-leaks in javascript. How does the lambda-function affect my memory? Is it better to define it first like var i = function(e)... and then passing it as a parameter to setInterval?)
My situation may have been a bit different, but here's what I did:
var self = this;
setInterval(function() { self.func() }, 50);
My scenario was that my code was inside a class method and I needed to keep correct scope as I didn't want the 'this' binding to resolve to the current window.
eg. I wanted to run MyClass.animate from MyClass.init using setInterval so I put this scope-keep code into MyClass.init
You can use native bind function.
function Loop() {
this.name = 'some name for test';
setInterval( (function(){//wrap the function as object
//after bind, "this" is loop refference
console.log(this);
}).bind(this), 1000 );// bind the object to this (this is Loop refference)
}
var loop = new Loop();
paste this example in the console to see the result
What's wrong with simply relying on the outer-scope defined variable?
(function() {
var x = {};
setInterval(function() {
funkyFunction.call(x)
}, speed);
})();
I had the same question, but there seems to be no built in solution, so here is a quick workaround I punched together:
function setScopedInterval(func, millis, scope) {
return setInterval(function () {
func.apply(scope);
}, millis);
}
usage:
function MyClass() {
this.timer = null;
this.myFunc = function() { console.log('do some stuff'); };
this.run = function() {
this.timer = setScopedInterval(function () { this.myFunc(); }, 1000, this);
};
this.stop = function() { clearInterval(this.timer); };
}
var instance = new MyClass();
instance.run(); // will log to console every second
// until this line is called
instance.stop();
This only covers the use-case where you pass an actual function, not a string of code to be executed.
As for your question about memory leaks when using this functionality: it is not so much the problem with using setInterval as it is with anonymous functions in itself.
If you use a reference to an object inside a lambda, this reference will keep the referenced object in memory for as long as the anonymous function exists. I think the function is destroyed with a call to clearInterval.
I don't think there is any benefit from assigning the function to a variable first, on the contrary, it will create another variable containing a reference that will not be garbage collected as long as the anon func exists...
You may also have a look at the YUI Framework. It's fine for building applications and easy to learn.
YUI2: YAHOO.lang.later(when, scope, fn, args, periodic);
YUI3: Y.later(when, scope, fn, args, periodic);
UPDATE as example
Using YUI and jQuery (Do not forget enable $.noConflict())
var jQuerySelector = jQuery("div[class^='form-field-']");
jQuerySelector.hide();
jQuery(jQuerySelector[0]).show();
YAHOO.lang.later(5000, jQuery, function(jQuerySelector) {
if((!(this.index)) || (this.index == (jQuerySelector.length))) {
this.index = 0;
}
jQuerySelector.hide();
this(jQuerySelector[this.index++]).show();
}, jQuerySelector, true);
In short
1º parameter: 5000 on every 5000 miliseconds, 3º parameter (a function) will be executed
2º parameter: jQuery Object in which will be referenced by using this
3º parameter: function which will be executed. It receives as parameter either an array or an object passed as 4º parameter
5º parameter: true if true, executes continuously at supplied interval until canceled
see http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_later
UPDATE
No need for $.noConflict() because YUI does not use $ in any way.
There are two important distinctions to make.
1) Do you want a reference to the passed parameter so that the timeout function can track changes made to it, or do you want a clone of the passed parameter?
2) Do you want to be able to capture a reference to the timeout in case you want to cancel it? (Yes!)
// Normal setTimeout: retains a reference to `test` and returns the bad value
var test = 'test: good';
var timer = setTimeout(function() { console.log(test); }, 1000);
test = 'test: bad';
// Test2 receives a clone of `test2` and returns the good value, but does so right away, not on a timeout
var test2 = 'test2: good';
var timer2 = setTimeout((function() { console.log(test2); })(test2), 1000);
test2 = 'test2: bad';
// Test3 receives a clone of `test3` and returns the good value, but doesn't return a reference to the timeout, and can't be canceled
var test3 = 'test3: good';
var timer3 = function(test3) { setTimeout(function() { console.log(test3); }, 1000); }(test3);
test3 = 'test3: bad';
// Test4 receives a clone of `test4` and returns the good value, and correctly returns timeout reference
var test4 = 'test4: good';
var timer4 = function(test4) { return setTimeout(function() { console.log(test4); }, 1000); }(test4);
test4 = 'test4: bad';
// Test5 retains a reference to `test5` and returns the bad value
var test5 = 'test5: good';
var timer5 = setTimeout((function() { console.log(test5); }).bind(test5), 1000);
test5 = 'test5: bad';
// Did we capture references to the timeouts?
console.log(typeof timer);
console.log(typeof timer2);
console.log(typeof timer3);
console.log(typeof timer4);
console.log(typeof timer5);

how to write a recursive method in JavaScript using window.setTimeout()?

I'm writing a JavaSCript class that has a method that recursively calls itself.
Scheduler.prototype.updateTimer = function () {
document.write( this._currentTime );
this._currentTime -= 1000;
// recursively calls itself
this._updateUITimerHandler = window.setTimeout( arguments.callee , 1000 );
}
Property description:
_currentTime: the currentTime of the timer in miliseconds.
_updateUITimerHandler: stores the reference so can be used later with clearTimeout().
my problem is where I'm using recursion with setTimeout(). I know setTimeout() will accept some string to execute, or a reference to a function. since this function is method of an object, I don't know how to call it from outside. so I used the second format of setTimeout() and passed in a reference to the method itself. but it does not work.
Try this:-
Scheduler.prototype.startTimer = function() {
var self = this;
function updateTimer() {
this._currentTime -= 1000;
self.hTimer = window.setTimeout(updateTimer, 1000)
self.tick()
}
this.hTimer = window.setTimeout(updateTimer, 1000)
}
Scheduler.prototype.stopTimer = function() {
if (this.hTimer != null) window.clearTimeout(this.hTimer)
this.hTimer = null;
}
Scheduler.prototype.tick = function() {
//Do stuff on timer update
}
Well the first thing to say is that if you're calling setTimeout but not changing the interval, you should be using setInterval.
edit (update from comment): you can keep a reference from the closure if used as a class and setInterval/clearInterval don't require re-referencing.
edit2: it's been pointed out that you wrote callee which will work quite correctly and 100% unambiguously.
Out of completeness, this works:
function f()
{
alert('foo');
window.setTimeout(arguments.callee,5000);
}
f();
so I tried out document.write instead of alert and that is what appears to be the problem. doc.write is fraught with problems like this because of opening and closing the DOM for writing, so perhaps what you needed is to change the innerHTML of your target rather than doc.write
You could hold a pointer towards it...
/* ... */
var func = arguments.callee;
this._updateUITimerHandler = window.setTimeout(function() { func(); }, 1000);
/* ... */

Categories

Resources