Javascript setTimeout function with 'this' [duplicate] - javascript

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
setTimeout and “this” in JavaScript
I am trying to put a timeout on an Object. With some test code (see below) I want to decrease the timerPos until it reaches 0. When I use the code below the first time timerInc() is called by startTimer(), it will reach 3 (as expected). When TimerInc() is called by the timeout i will receive 'undefined' for the timerPos variable. What am I doing wrong?
function start(){
var alert = new Alert(3);
alert.startTimer();
}
function Alert(timer) {
this.timerMinutes = timer;
this.timerPos = 0;
this.startTimer = function() {
this.timerPos = this.timerMinutes+1;
this.timerInc();
};
this.timerInc = function() {
if (this.timerPos > 0){
this.timerPos--;
// first time this function gets called timerPos is 3
// the second time when its called by the timeout it
// will be 'undefined'
setTimeout(this.timerInc,1000);
}
};
}
(using this.timerInc() in the timeout instead of this.timerInc does not work for me, neither does using quotes)

You need to bind the "this" variable to another one that you use explicitly since the value of "this" changes based on who is calling the function!
function Alert(timer) {
var that = this; // Store this Alert instance as "that".
this.timerMinutes = timer;
this.timerPos = 0;
// ...
this.timerInc = function() {
// Use the Alert instance "that", not whatever is bound to "this" at runtime.
if (that.timerPos > 0){
that.timerPos--;
setTimeout(that.timerInc, 1000);
}
};
}
The issue is that the setTimeout() function will call its function argument from global scope, not the scope of the enclosing object at the time it is registered. So in global scope the "this" variable is bound to the "global" object (likely the browser window).
You can verify like so:
setTimeout(function(){alert(this);}, 500); // => alerts "[object DOMWindow]"

First of all you should use prototype to declare the methods of your class Alert. And changing the scope of the function you're calling is gonna do the job:
function start(){
var alert = new Alert(3);
alert.startTimer();
}
function Alert(timer) {
this.timerMinutes = timer;
this.timerPos = 0;
}
Alert.prototype.startTimer = function() {
this.timerPos = this.timerMinutes+1;
this.timerInc();
};
Alert.prototype.timerInc = function() {
if (this.timerPos > 0){
this.timerPos--;
console.log(this.timerPos);
setTimeout(function(_this){_this.timerInc()},1000,this);
}
};
DEMO: http://jsfiddle.net/kmendes/HNYKa/1/

Related

Why isn't the variable in this object modified by its callback function?

I'm trying to get a global object to modify one of its own variables in a callback function initialised by one of its own methods. The callback appears to work, but the variable doesn't seem to have been modified when testing the global variable.
Why is the global object not being modified? Are the changes to the global object kept in some sort of staging area pending completion of the callback function?
let obj;
function test_object_flag() {
// 5 - check whether the "timer_finished" flag has been set
console.log("is the timer finished? " + obj.timer_finished); // should return "true"???
}
class TestClass {
constructor() {
this.timer_finished = false;
}
start_timer(ptr_callback_function) {
// 3 - set up a callback for the timer
setTimeout(function() {
// 4 - once the timer is done, set a "timer_finished" flag, and call the callback
this.timer_finished = true;
ptr_callback_function();
}, 1000);
}
}
$( document ).ready(function() {
// 1 - make a new onbject of type TestClass
obj = new TestClass();
// 2 - start the timer
obj.start_timer(test_object_flag);
});
The problem is that setTimeout creates it's own this. Solution may looks like:
start_timer(ptr_callback_function) {
// savig this that your need
const self = this;
setTimeout(function() {
// use needed context
self.timer_finished = true;
ptr_callback_function();
}, 1000);
}
Another option is to use arrow functions:
start_timer(ptr_callback_function) {
setTimeout(() => {
this.timer_finished = true;
ptr_callback_function();
}, 1000);
}

jQuery closure: How to get a value from a function click

I don't quite understand JS closures and I think it can solve my problem. here it is:
I have something like that :
$(document).ready(function () {
$("#buttonConfirm").click(function () {
popup_confirme();
});
});
function popup_confirme() {
var r = popup_modal();
}
function popup_modal() {
var int_val = 0;
$(".button").click(function () {
int_val = ($(this).val() == 'yes' ? '1' : '0');
});
return int_val;
}
I would like to get my int_val returned by the button click event. I need to get the 'r' value as 0 or 1. I know I should use closures, but I dont know how to do it. Thank you for your expertise !
You can't do this, it's impossible, for reasons unrelated to closures.
Your not calling the code that sets int_val, you're only defining the code, and saying "when .buttons are clicked, invoke this code". The code will not have been executed at the point you run return int_val, it will be executed at some point in the future when the button is clicked.
This block of code cannot logically work:
// First line run
var int_val = 0;
// Second line run
$(".button").click(function () {
// This line runs in the future, or maybe never, if the button isn't clicked
int_val = ($(this).val() == 'yes' ? '1' : '0');
});
// Third line run
return int_val;
If you want to communicate values back out of asynchronous functions, you should use promises:
function popup_modal() {
var dfd = $.Deferred();
$(".button").click(function () {
int_val = ($(this).val() == 'yes' ? '1' : '0');
// Allow int_val to find its way back to the calling code's `done` handler
dfd.resolve(int_val);
});
return dfd.promise()
}
The calling code will receive a promise object, which it can add callbacks to:
function popup_confirme() {
var r;
popup_modal().done(function (int_val) {
r = int_val;
}
}
I can't intuit beyond this point what you meant int_val to do up in your calling code.
A closure occurs when an inner function references something defined outside it. To illustrate:
function outer() {
var foo = 1;
element.click(function () {
// this function creates a closure on foo.
alert(foo);
});
};
What you seem to want done is that your int_val variable be accessible where both popup_modal and popup_confirme can get to it.
Many ways to do it based off your example, but something as simple as this can work:
(function () {
var int_val = 0;
var popup_modal = function () {
int_val = $(this).val() === 'yes' ? 1 : 0;
};
var popup_confirme = function () {
// your original code here doesn't really do anything
alert(int_val);
};
$('.button').click(popup_modal);
$('#buttonConfirm').click(popup_confirme);
}());
Technically all JavaScript functions are closures as they are objects with a scope chain attached to them. A closure is simply the combination of a function object and a scope (a set of variable bindings).
Scope is actually pretty simple really. Javascript uses lexical scoping which means that function are executed in the variable scope that was in effect when they were defined. Simply put, An outer function cannot read a value from an inner function unless is is specifically returned. An inner function can read all values declared in an outer function.
When most people talking about closures they are actually referring to the act of returning an item from an inner nested function to the outer function in which it has been defined.
e.g
// I am the outer function.
function outer (){
var outerVariable = "I am available to inner functions.";
// I am an inner function. I was declared in the scope of the outer
// function and as such all the variables declared in that scope are
// available to me.
function inner (){
// This will return => "I am available to inner functions." as we can
// read the outer declaration.
var innerReadValue = outerVariable;
// This will be available only to the inner function as it is
// not returned.
var privateVariable = "I am private";
// This will be available to the outer function as we are returning it
// on the next line.
var publicVariable = "I am available to outer functions";
// Make publicVariable public. This is what most people refer to
// when talking about closures.
return publicVariable;
}
// Read the inner functions publicVariable declaration.
// Returns => "I am available to outer functions"
var outerReadValue = inner();
}
In your example you are trying to get a value that was declared and not returned on the inner scope. As you should understand by now, this is invisible to the outer function so cannot work.
This can be rewritten as such:
// This is called an Immediately Invoked Function Expression. IIFE, It
// basically wraps your code in a function hiding all internal declarations
// from the global scope and then invokes it. You don't want to pollute the
// global scope.
(function(){
// Declare this outside so all functions can read it.
var int_val = 0;
// Declare this outside so all functions can read it.
var popup_confirm = function(){
// "int_val" is available here.
return int_val;
};
// Although your function runs automatically, we delay binding until
// the DOM is ready.
$(document).ready(function(){
$("#buttonConfirm").click(function(){
// Provide feedback. "popup_confirm" is available here
// since is declared in the outer scope.
return popup_confirm();
});
$(".button").click(function(){
// Set the value of int_val that has been declared in the outer
// scope.
int_val = $(this).val() === "yes" ? 1 : 0;
});
});
}());
Hopefully this makes it all a little bit more clear to you.

I want to use a anonymous function inside an object

I have this code for a newsfeed that I want to use.
I want it to look kind of like this:
function News(){
//Load new comments every 5 sec
setTimeout((function(){
console.log(this); //Returns Object #News
this.loadNewsFeed();
}).call(this),5000);
this.loadNewsFeed = function(){
// Implementation here
}
}
The problem here is that it says the Object News doesn't have an method called loadNewsFeed!
I've already got it to work if I put the anonymous function outside the object News.
Like this:
var news = new News();
//Load new comments every 5 sec
(function loopNews(){
news.loadNewsFeed();
setTimeout(loopNews,5000);
})();
So how can I do this inside the object News?
This should work:
function News()
{
var self = this;
this.loadNewsFeed = function(){
// Implementation here
};
//Load new comments every 5 sec
setInterval(function handler() // setInterval for endless calls
{
console.log(self); //Returns Object #News
self.loadNewsFeed();
return handler;
}(), 5000);
}
Explanation:
call(this) invokes the handler directly - and returns undefined to setInterval which means that it's executed immediately but no handler is set.
The handler-function executes in global context so this is the window-object. The local variable self "injects" the current (and desired) this - as self.
Edit 2:
Now executes immediately and registers a handler.

How to determine if a function has been called without setting global variable

I am looking for a good technique to get away from what I am tempted to do: to set a global variable.
The first time someone runs a function by clicking a button it triggers an initial function to turn a few things into draggables. Later, if they click the button a second time I want to determine if the init function has been initialized, and if so to not call it again. I could easily do this by setting a global variable from the init function and then checking that variable from the click function, but I'm wondering how to do this without setting a global variable. I would really like an example of a way to do this.
You could add a property to the function:
function init() {
init.called = true;
}
init();
if(init.called) {
//stuff
}
While #Levi's answer ought to work just fine, I would like to present another option. You would over write the init function to do nothing once it has been called.
var init = function () {
// do the initializing
init = function() {
return false;
}
};
The function when called the first time will do the init. It will then immediately overwrite itself to return false the next time its called. The second time the function is called, the function body will only contain return false.
For more reading: http://www.ericfeminella.com/blog/2011/11/19/function-overwriting-in-javascript/
Why don't you just check to see if your draggables have a class of draggable on them?
if ($('.mydiv').is('.draggable')) {
//do something
}
Function.prototype.fired = false;
function myfunc() {
myfunc.fired = true;
// your stuff
};
console.log(myfunc.fired) // false
myfunc();
console.log(myfunc.fired) // true
What you could do is unhook the init function from the prototype.
​var Obj = function () {
this.init = function () {
document.write("init called<br/>");
this.init = null;
}
}
var o = new Obj();
if (o.init) document.write("exists!<br/>");
o.init();
if (o.init) document.write("exists!<br/>");
o.init();
​
The first if will be true and print exists! but since the function removes itself, the second if will fail. In my example, I call the second init unconditionally just to show that nothing will happen, but of course you could call it only if it exists:
if (o.init) o.init();
http://jsfiddle.net/coreyog/Wd3Q2/
The correct approach is to use the Javascript Proxy APIs to trap the function calls using apply handler.
const initFun = (args) => {
console.log('args', args);
}
const init = new Proxy(initFun, {
apply(target, thisArg, args){
target.calls = target.calls ? target.calls + 1 : 1;
return target.apply(thisArg, args);
}
});
init('hi');
console.log(init.calls); // 1
init('hello');
console.log(init.calls); // 2

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);

Categories

Resources