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);
}
Related
I am using Angular 10 and have the following setInterval code working in my local:
ngOnInit() {
this.myfunc();
setInterval(this.myfunc.bind(this), 120000);
}
However, the same code is not working on the server.
In other words, myfunc() is not triggering after 2 mins when running on the server.
Debugging Details:
In my local, this.myfunc() is called when the component is loaded for the first time. It is called again after 2 mins as per the setInterval()
However, when running on the server, this.myfunc() is called when the component is loaded for the first time. But it is not called again after 2 mins as per the setInterval()
Problem
setInterval sometimes drifts, as seen in this post.
Solution
Taken from this solution, you would first make a non-drifting class:
function AdjustingInterval(workFunc, interval) {
let that = this;
let expected, timeout;
this.interval = interval;
this.start = function() {
expected = Date.now() + this.interval;
timeout = setTimeout(step, this.interval);
}
this.stop = function() {
clearTimeout(timeout);
}
function step() {
let drift = Date.now() - expected;
workFunc();
expected += that.interval;
timeout = setTimeout(step, Math.max(0, that.interval - drift));
}
}
and then, you would just use this instead for your code:
ngOnInit() {
let ticker = new AdjustingInterval(this.myfunc.bind(this), 120000);
this.myfunc();
ticker.start();
}
I was able to resolve the issue by changing setInterval() to callback as follows:
setInterval(() => {
this.myfunc(); }, 120000);
}
Updated ngOnInit() looks as below:
ngOnInit() {
this.myfunc();
setInterval(() => {
this.myfunc(); }, 120000);
}
}
I have a vue application, but the countdown not work good.
Actually i dont know why.
View {{ $parent.timer }} i see the good value.
Vue data:
data: function() {
return {
timer : 3,
...
and here is my countdown function:
countdown : function(time,callback)
{
//time is equal 5
this.timer = time;
//this.timer is equal 5
var timerInterval = undefined;
timerInterval = setInterval(function() {
if(this.timer == 1) {
this.timer = 0;
callback();
return clearInterval(timerInterval);
}
// First time undefined, in 2nd is NaN
this.timer -= 1;
// NaN
}, 1000);
}
call function:
this.countdown(data.time,function(){ //smtng });
What i do bad? Its work in my older Vue application.
I hope someone can help to me :)
Thanks so much!
It is an issue with scope of this, as explained below:
function() {...} creates a new scope inside. If you use this inside this function, it does not refer to outer scope. Therefore your this.timer of Vue component does not get updated from inside your setInterval().
() => {...} works like a function but does not create a new scope inside.
Check if the following code works:
timerInterval = setInterval(() => {
if(this.timer == 1) {
this.timer = 0; // `this` points to the scope of Vue component
callback();
// ...
}
// ...
}, 1000);
More info on arrow functions: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions
I have a HTML page with two buttons and I want to use setInterval to evaluate the function multiple times when I click the start button. This works just fine but I can't clear the interval. What am I doing wrong?
function intFunc (func, time) {
interval = setInterval(func, time);
}
$("#startButton").on("click", intFunc(function () {
$.post('link/to/php.php', function(data) {
//do something with data
});
}, 1000));
$("#stopButton").on("click", function () {
clearInterval(interval);
});
First, as the comments suggest you should declare interval above to make sure the scope is clear.
Second, you have a syntax problem. You're invoking intFunc straight away, and passing the result to $("#startButton").on(). This isn't what you want. What you should be passing as that second argument is a callback function. You could reorganize your code like this perhaps:
Third, declaring within a self invoking anonymous function will prevent pollution of the global scope.
(function() {
var interval;
$("#startButton").on("click", function(){
interval = setInterval(function () {
$.post('link/to/php.php', function(data) {
//do something with data
});
}, 1000);
});
$("#stopButton").on("click", function () {
clearInterval(interval);
});
})();
This is how you should (or could) have written it:
var interval;
$("#startButton").on("click", function(){
interval = setInterval(function () {
$.post('link/to/php.php', function(data) {
//do something with data
});
}, 1000);
});
$("#stopButton").on("click", function () {
clearInterval(interval);
});
You cannot use functions like that, because when you write:
$("#startButton").on("click", intFunc(..));
You are executing intFunc immediately, and that doesnt return anything.
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);
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();