How can I use "call" with "setInterval" to get an object literal to invoke one of its own methods?
Here's an example.
This works, and I understand why it works.
The timer object calls its own tick method once each second
var timer =
{
start: function()
{
var self = this;
setInterval(function(){self.tick();}, 1000);
},
tick: function()
{
console.log("tick!");
}
};
timer.start();
I tried to simplify this code by using "call".
This next example is the best that I came up with.
But it doesn't work: the tick method is called only once, and then I get a type error.
var timer =
{
start: function()
{
setTimeout.call(this, this.tick(), 1000);
},
tick: function()
{
console.log("tick!");
}
};
timer.start();
I think I don't really understand how call works.
Can anyone explain what I'm doing wrong?
You are .calling .setInterval not your callback function which the browser calls:
setInterval( this.tick.bind(this), 1000 );
Should work. See .bind
This is what I ended up with:
var timer = {
time: 0,
start: function() {
var timerTick = this.tick.bind(this);
window.setInterval(function() {
timerTick();
}, 1000);
},
tick: function() {
this.time += 1;
console.log(this.time);
}
};
timer.start();
Related
I was playing with this example timer and then wanted to see if I could capture/pass data into the timer so I started with a simple message. If I remove the message to timer it ticks accordingly. I started logging other lifecycle methods but I am curious if this is JS thing which I don't think it is versus something in react life cycle. I forked a JS bin here with ex
'code'
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function(msg) {
console.log('msg is',msg);
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick('hi'), 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
return (
<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
}
});
React.render(<Timer />, document.getElementById("content"));
All you need to do is to change this bit in your code:
this.interval = setInterval(() => this.tick('hi'), 1000);
Alternatively, you can also send in this to the callback:
this.interval = setInterval(function(t) {t.tick('hi')}, 1000, this);
See the updated fiddle, here.
setInterval() takes (at least) two parameters, the first one is the callback and needs to be a function. You had provided the code directly; that code must be inside a function.
var intervalID = scope.setInterval(func, delay);
func
A function to be executed every delay milliseconds. The function is not passed any parameters, and no return value is expected.
Change code to:
componentDidMount: function() {
this.interval = setInterval(()=>this.tick('hi'), 1000);
}
I'm trying to disable 2 functions when a certain time period is reached and enable the other 2 after that time period. So the second 2 functions would have to be disabled to begin with.
I was thinking of using the following code to wrap around the functions:
Code:
var startTime = new Date().getTime();
var interval = setInterval(function(){
if(new Date().getTime() - startTime > 5000){
clearInterval(interval);
return;
}
function 1() {}
$(function 2() {});
}, 1000);
function 3() {}
$(function 4() {});
Can you help?
If you want to control whether functions do something or not, based on how much time has elapsed, it would probably be easier to set a flag after the interval you need, and then have your functions check that flag to decide if they are going to do something:
var timedOut = false;
setTimeout(function () {
timedOut = true;
}, 5000);
function one() {
if (!timedOut) {
// do something
}
}
function two() {
if (!timedOut) {
// do something
}
}
function three() {
if (timedOut) {
// do something
}
}
function four() {
if (timedOut) {
// do something
}
}
This should get you started; I've simply redefined the original func1/func2 functions after a set time (5 seconds, as your example uses). This could do any number of things (such as remove the function definition altogether).
(function(document,window,undefined){
// Used simply to show output to the window.
var db = document.getElementById('db');
// Here we define the initial state of our two functions.
// Nothing magical here, just outputting a description.
window.func1 = function(){
db.innerHTML += 'Hello from original func1\r\n';
}
window.func2 = function(){
db.innerHTML += 'Hello from original func2\r\n';
}
// Here we keep the same format you used (using the Date to
// define when one's been deprecated over the other).
var startTime = new Date().getTime(),
interval = setInterval(function(){
var currentTime = new Date().getTime(),
delta = currentTime - startTime;
if (delta > 5000){
// In here, now that the specified amount of time has
// elapsed, we redefine the meaning of the two original
// functions. We could also simply remove them.
window.func1 = function(){
db.innerHTML += 'Hello from NEW func1\r\n';
}
window.func2 = function(){
db.innerHTML += 'Hello from NEW func2\r\n';
}
clearInterval(interval);
}
}, 1000);
})(document,window);
// This is here just to show you how one definition is changed
// in place of another.
setInterval(function(){
func1();
func2();
}, 1000);
<pre id="db"></pre>
If you mean 'disabling' the functions after certain amount of seconds then this should do the trick.
var secondsLimit = 10,
a = 0,
b = setInterval(function () { a += 1; }, 1000 });
function A() {
if (a > secondsLimit) {
return;
}
// do stuff
}
You can change the functions if you call them e.g. by a global variable scope.
In the following example based on your code, the functions switch after 4 seconds.
var function1 = function() {
console.log("function 1 active");
};
var function2 = function() {
console.log("function 2 active")
}
var startTime = new Date().getTime();
setTimeout(function() {
function1 = function() {
console.log("now function 3 is active instead of function 1");
}
function2 = function() {
console.log("now function 4 is active instead of function 2");
}
}, 4000);
//the following code is just for testing reasons
var interval = setInterval(function() {
function1();
function2();
}, 1000)
Ok, so basically i'm creating an Interval class to handle repeating actions.
I have something like this:
function Interval(fn, speed) {
this.fn = fn;
this.speed = speed;
this.nt = setInterval(fn, speed);
}
And then i have 3 methods:
this.pause = function() {
clearInterval(this.nt);
}
this.start = function() {
this.nt = setInterval(this.fn, this.speed);
return this.nt;
}
this.wait = function(time) {
this.pause();
setTimeout(function() {
this.start();
}, time);
}
The problem appears in the third method. this.pause(); and this.start(); works as expected. But when I nest this.start into a setTimeout function it stops working. I don't understand why. Here's an example:
var i = 0:
var nt = new Interval(function() {
alert('Itineration: '+ i );
if(i>5);
nt.pause();
setTimeout(nt.start, 2000);
// nt.wait(2000);
}, 500);
Neither nt.wait(2000); nor nt.pause(); setTimeout(nt.start, 2000); is working.
this inside the timeout handler is not the Interval object, it is referring to the window object(not strict mode) so this.start() will not work
One solution is to pass a custom context using Function.bind()
this.wait = function (time) {
this.pause();
setTimeout(function () {
this.start();
}.bind(this), time);
// setTimeout(this.start.bind(this), time) as #elclanrs suggested
}
You are running into a context issue with your code. When the setTimeout function executes your callback the definition of "this" is no longer your Interval object. You need to modify your code so that you maintain a proper reference to the Interval object.
this.wait = function(time) {
var interval = this;
interval.pause();
setTimeout(function() {
interval.start();
}, time);
}
Edit
I just saw the other answer using .bind which is a much cleaner solution from a readability standpoint. One important note about .bind is that behind the scenes it basically generates another function to call your original function using the .call or .apply methods to set the correct value of this
In most cases the readability gained from using .bind is worth it. However, if this is going to be a core component to a larger system, it is a good idea to squeeze every ounce of performance you can out of it. Which would be an argument for avoiding .bind in this specific situation.
Working example based on the other answers.
function Interval(fn, speed) {
this.fn = fn;
this.speed = speed;
this.nt = setInterval(fn, speed);
this.pause = function () {
clearInterval(this.nt);
}
this.start = function () {
this.nt = setInterval(this.fn, this.speed);
return this.nt;
}
this.wait = function (time) {
this.pause();
setTimeout(function () {
this.start();
}.bind(this), time);
}
}
var i = 0;
var nt = new Interval(function () {
document.write('<pre>Itineration: ' + i + '</pre>');
i++;
nt.wait(2000);
}, 500);
I know a million JavaScript timer questions have been asked already, but I can't figure this one out.
The #drive element is updated the first time (to 0) but not after that, as if either setTimeout() is not working or this.count++ is not working. Anyone know why this isn't working?
Thanks...
var timer = {
timerRunning: false,
count: 0,
delay: 1000,
tick: function() {
$("#drive").html(this.count);
this.count++;
setTimeout(function(){
if (this.timerRunning)
this.tick();
}, this.delay);
},
start: function() {
this.timerRunning = true;
this.tick();
},
stop: function() {
this.timerRunning = false;
}
};
timer.start();
The value of this inside your setTimeout() callback is no longer your timer object (it will be the window object). That's why it isn't working.
You can fix it like this by setting a closure variable to your object and using that instead:
tick: function() {
$("#drive").html(this.count);
this.count++;
var self = this;
setTimeout(function(){
if (self.timerRunning)
self.tick();
}, this.delay);
},
The callback is bound to a different this variable. Here is a workaround:
tick: function() {
var self = this;
$("#drive").html(this.count);
this.count++;
setTimeout(function(){
if (self.timerRunning)
self.tick();
}, this.delay);
}
Greetings,
I have the following JS code:
var reloadTimer = function (options) {
var seconds = options.seconds || 0,
logoutURL = options.logoutURL,
message = options.message;
this.start = function () {
setTimeout(function (){
if ( confirm(message) ) {
// RESET TIMER HERE
$.get("renewSession.php");
} else {
window.location.href = logoutURL;
}
}, seconds * 1000);
}
return this;
};
And I would like to have the timer reset where I have the comment for RESET TIMER HERE. I have tried a few different things to no avail. Also the code calling this block is the following:
var timer = reloadTimer({ seconds:20, logoutURL: 'logout.php',
message:'Do you want to stay logged in?'});
timer.start();
The code may look familiar as I found it on SO :-)
Thanks!
First of all, you need to use the new operator in var timer = new reloadTimer, and also reloadTimer should be capitalized into ReloadTimer to signify that it needs to be used with new.
The reason why you need new is because the function references this and when used without new this will be the global scope instead of the instance it self.
To reset a timer you just call window.clearTimeout with the timers reference as the parameter
var timer = window.setTimeout(....
...
window.clearTimeout(timer);
UPDATE
By RESET do you actally mean to restart the timer?
If so, just use setInterval instead of setTimeout
UPDATE 2
And here is a slightly better approach (if you still want to use such a class to encapsulate something so trivial)
var ReloadTimer = function(options){
var seconds = options.seconds || 0, logoutURL = options.logoutURL, message = options.message;
var timer;
return {
start: function(){
timer = setInterval(function(){
if (confirm(message)) {
$.get("renewSession.php");
}
else {
clearInterval(timer);
window.location.href = logoutURL;
}
}, seconds * 1000);
}
};
};
var myTimer = new ReloadTimer({
seconds: 20,
logoutURL: 'logout.php',
message: 'Do you want to stay logged in?'
});
myTimer.start();
You could execute the function again with the same parameters?