Clear Interval doesn't work in VUE - javascript

I am trying to build a Pomodoro Timer using VUE.js, below is the relevant code snippet that I had problems on. So the problem is that the clearInterval Method doesn't seem to work here.
data: {
workTimeInMin: 1,
breakTimeInMin: 1,
timeRemainInMillisecond: 0,
timerOn: false,
rest: false
},
methods: {
startOrStopTheTimer: function() {
var self = this;
var interval;
if (this.timerOn){
this.timerOn = !this.timerOn;
window.clearInterval(interval);
console.log("clearInterval");
}else {
this.timerOn = !this.timerOn;
interval = setInterval(function(){
console.log("123");
if (self.timeRemainInMillisecond <= 0) {
console.log("1");
self.rest = !self.rest;
if (self.rest) {
self.timeRemainInMillisecond = self.restTimeInMin*60*1000;
}else {
self.timeRemainInMillisecond = self.workTimeInMin*60*1000;
}
}
this.timeRemainInMillisecond = this.timeRemainInMillisecond-1000
}, 1000);
}
},
You can find a live demo here.
In the page, when I click start an set Interval method is called. Then I clicked the stop, it is supposed to clear the interval, however it doesn't seem to work as I intend it to. You can inspect the console and easily find the "123" keeps popping up which indicates that the interval is not cleared.
After searching a while in the StackOverFlow, I found that if I define the variable interval to in the global context it will work as I intend it. But I wish to know why it is so.
Your help will be highly appreciated. Thanks in advance!

Your problem is that var interval is a declared new every time the function is called. So the variable that holds a reference to your interval is not accessible.
Just use this.interval and the variable will added to the data-section of your component under the hood.
You can also just use this mixin (plugin) / have a look how it works there:
Vue interval mixin

You can clear setInterval and clearinterval. here is sample code please modify according to your need
if (val && !this.t) {
this.t = setInterval(() => {
location.reload()
}, 10 * 1000)
} else {
clearInterval(this.t)
}

I arrived at this page because Vue still seems to handle setInterval in the strange way as described by the OP. I tried all the fixes above and found only two options work:
Global scope the interval variable (as mentioned above and less than an ideal workaround)
Use Vuex to store the timer and handle it the usual Vuex way.
I was happy to find that using Vuex bypasses the problem, since I'm using Vuex anyway. It doesn't shed light on why this issue exists, but it does give us a way to move forward with little sacrifices made.
So just declare the variable in the Vuex state, use mutations to start and stop the timer and all works as expected.

This works like a charm:
data () {
return {
polling: null
}
},
methods: {
pollData () {
this.polling = setInterval(() => {
console.log('fired...')
}, 3000)
}
},
beforeDestroy () {
clearInterval(this.polling)
},
created () {
this.pollData()
}

Related

Store variables between ReactJs component method calls

togglePreloader: function(toggleState) {
var timeout = null;
var time = 0;
if (toggleState) {
if (this.state.preloading) console.log("Preloader alredy shown");
if (timeout) {
clearTimeout(timeout);
timeout = null;
} else if (!this.state.preloading) {
console.log("Show preloader");
time = Date.now();
this.setState({ preloading: true });
}
} else {
if (!timeout) {
var elapsed = Date.now() - time;
if (elapsed < Config.PRELOADER_MIN_DISPLAY_DURATION) {
console.log("Preloader hiding timeout was started; elapsed: " + elapsed + "/" + Config.PRELOADER_MIN_DISPLAY_DURATION);
timeout = setTimeout(function() {
timeout = null;
this.setState({ preloading: false });
console.log("Hide preloader by timeout");
}.bind(this), Config.PRELOADER_MIN_DISPLAY_DURATION - elapsed);
} else {
this.setState({ preloading: false });
console.log("Hide preloader; elapsed: " + elapsed + "/" + Config.PRELOADER_MIN_DISPLAY_DURATION);
}
} else console.log("Preloader hiding is waiting for timeout");
}
}
This is the method of reactJs component. It trigger to show and hide preloader. If preloader was displayed less than minimal duration (e.g 500ms) it sets timeout on hiding.
The question is where to store variables timeout and time between calls of togglePreloader. Mutating this.props is not a good idea. Changes in this.state triggers rerendering. Moving variables out of component ? Or using state with shouldComponentUpdate ? What is the best way ? Im new to reactJs
It's not just not a good idea, you don't get to play with this.props, it's the collection of data that the component's parent controls. You can use state, which will render, or you can just do the obvious thing: just use this.timeout = ..., since your React component is still just a JavaScript object with its own instance scoping.
this.props.xyz for values that you were assigned "from above"
this.state.xyz for values that you control and directly influence what the UI should look like
this.xyz for any transient values that have no influence on the UI and can technically be reset without any adverse effects.
However, consider that the timeout value is universal, so should probably be a static:
var YourComponent = React.createClass({
statics: {
timeout: 500
},
...
checkStuff: function() {
if (this.currentTime >= YourComponent.timeout) {
this.doAThing();
}
},
...
});
And if the idea is that different things happen to the UI based on that timeout, then really you should trigger some state value that your component can then use in render() to make sure it's now showing, or doing, the right thing. So you use a this.currentTime to track timing so far, and then a state variable once you know you've passed the threshold.
If you're new to react, it's 100% worth running through the "Getting started" and "tutorial" sections on the React website. Just sit down on them, do all of it in one go -it takes less than 30minutes- and you'll have a much better idea of how you're supposed to work with React. And then if you still need more insight, there are a lot of great articles on React on the googles.

Testing nested object with jasmine

Here is my Test
describe("setTimer", function () {
it("set status timer values from parameters and sets timer.visible to true", function(){
var boxNumber = 1,
time = 15;
myObject.setTimer(boxNumber, time);
expect(anotherObject.status.timer.boxNum).toBe(boxNumber);
expect(anotherObject.status.timer.seconds).toBe(time);
})
});
Here is the code
setTimer: function (boxNum, seconds) {
anotherObject.status.timer.boxNum = boxNum;
anotherObject.status.timer.seconds = seconds;
anotherObject.status.timer.visible = true;
},
Here is the error I am getting
TypeError: Cannot read property 'timer' of undefined
I tried setting the object using anotherObject = {} I tried setting anotherObject.status = {} and lastly tried setting anotherObject.status.timer = {}, however I still get the error. Any ideas, how can I mock the object?
Without knowing how/where 'anotherObject' is constructed I would think that you would need to initialize the 'anotherObject' before you execute the setTimer function in your test.
Do you have an init() or setup() function that exists on 'anotherObject' that would initialize the 'timer' object for you?
Although the method looks like it is just trying to make sure that the method is setting all the corresponding properties.
You could do the following before calling setTimer in your test
describe("setTimer", function () {
it("set status timer values from parameters and sets timer.visible to true", function(){
var boxNumber = 1,
time = 15;
//Initialize the anotherObject
anotherObject.status = { timer : {} }
myObject.setTimer(boxNumber, time);
expect(anotherObject.status.timer.boxNum).toBe(boxNumber);
expect(anotherObject.status.timer.seconds).toBe(time);
})
});
This of course comes with the caveat that you have now defined an 'anotherObject' inside your test using the global scope (since excluding the var on any variable definition in javascript makes it global scope). This could effect other test cases that expect the timer object to be setup a certain way but your test case has now set the timer values to 1 and 15 respectively (could be alot of other values all depending on what the test case is doing).
So to help with this, resetting the 'anotherObject' at the beginning or end of your tests would help with pollution
afterEach(function(){
anotherObject.status = { timer : {} }
})
or
beforeEach(function(){
anotherObject.status = { timer : {} }
})
Of course if you have an init(), create() or setup() function on the 'anotherObject' that could be used it would of course give you more realistic results since the object would be much closer to what it would look like in production.
You are not working on the same "anotherObject" object in both source and test codes.
Each code has it's own object and setting values to one will not set in the other.

Send setTimeout name, element id, and time as parameters of a function?

This is probably something really obvious, but I've searched around and tried a few things, and can't get it to work, so maybe someone can point out my error here.
I have a setTimeout that I will end up using over and over (and I know there is the setinterval, but I actually need to control when the timer starts and stops, and whether it starts again each time). Anyway, I figured if I'm writing it over and over, I should be able to use a function and pass it the parameters needed.
if ($('#selectRole').val() === 'Dispatch') {
//show Add Notes button
var funcAddNotesTimer = function(timerName,buttonName, timeToHide) {
console.log(timerName);
console.log(timeToHide / 1000);
timerName = setTimeout(function() {
$('buttonName').show();
}, timeToHide);
};
funcAddNotesTimer('addNotesTimer', '#disAddNotes', 30000);
I'm trying to set the timer function name to 'addNotesTimer', and when the timer is up I want to show the button with id #disAddNotes, and I want the timer to run for 30000 msec.
To me, what I have looks right, but I never get anything in my console log, so I don't think it's even getting into the function.
What am I doing wrong here?
I dont think its possible to use a string argument as the name of setTimeOut
Heres how you could approach it
// var timer = null; // dont really need that
var funcAddNotesTimer = function(buttonName, timeToHide) {
var timerName = setTimeout(function() {
//$('buttonName').show();
$(buttonName).show(); // buttonName is already a string so no need to add quotes around it.
}, timeToHide);
return timerName;
};
if ($('#selectRole').val() === 'Dispatch') {
var timer = funcAddNotesTimer('#disAddNotes', 30000);
// do something with timer
}
when you don't see any output in the console the reason must be something else(e.g. there is a bracket missing at the end of the code)
To set a variable with a dynamic name use the subscript-notation:
window[timerName] = setTimeout(/**/);
it will set a global variable named addNotesTimer.
As you currently do it you're simply overwriting the argument passed to the function.
Summary:
if ($('#selectRole').val() === 'Dispatch') {
//show Add Notes button
var funcAddNotesTimer = function(timerName,buttonName, timeToHide) {
console.log(timerName);
console.log(timeToHide / 1000);
window[timerName] = setTimeout(function() {
$(buttonName).show();
}, timeToHide);
};
funcAddNotesTimer('addNotesTimer', '#disAddNotes', 5000);
}

Javascript - Need to use clearInterval outside setInterval function

Basically, what I have is a setInterval inside a function. What I want to do is, control it's behavior from outside.
Here's what I have -
function wheee() {
var i = 1;
slideee = setInterval(function() {
sliderContent.style.marginLeft = margin(i);
if(i < imagesNoOneLess) {
i++;
} else {
i = 0;
}
}, 5000); }
Now, I want to clear the interval from outside the wheee() function. How can I do that?
I also want to run the interval again, from outside. How?
Global variables are not dangerous, but a pretty way of coding it if you only have one slider is to use an object literal to simulate a singleton object.
var Slider= {
slider: null,
Start: function(i) {
this.slider = setInterval(function() {
// Slider code
console.log('running: ' + i);
i++;
}, 1000);
},
Stop: function() {
window.clearTimeout(this.slider);
}
};
Slider.Start(1); // Start the slider
Slider.Stop(); // Stop the slider
Well the way you've got the code now, it'll probably just work, because you didn't declare "slideee" with var.
As long as you somehow export the return value from setInterval() you're OK. You can either make that variable explicitly global (better than having it be implicit), or else have your "wheee" function return the value to its caller.
Set the scope of slideee to be out of wheee.
Use objects in order to keep the global scope clean.

Clear javascript on dynamic load

I have a javascript plugin for a special image scroller. The scroller contains a bunch of timeout methods and a lot of variables with values set from those timeouts.
Everything works perfectly, but for the site I am working on it is required that the pages are loaded dynamically. The problem with this is when i for instance change the language on the site this is made by jquery load function meaning the content is dynamically loaded onto the site - AND the image slider aswell.
NOW here is the big problem! When I load the image slider for the second time dynamically all my previous values remains as well as the timers and everything else. Is there any way to clear everything in the javascript plugin as if it where like a page reload?
I have tried a lot of stuff so far so a little help would be much appreciated!
Thanks a lot!
You might want something like that to reload scripts:
<script class="persistent" type="text/javascript">
function reloadScripts()
{ [].forEach.call(document.querySelectorAll('script:not(.persistent)'), function(oldScript)
{
var newScript = document.createElement('script');
newScript.text = oldScript.text;
for(var i=0; i<oldScript.attributes.length; i++)
newScript.setAttribute(oldScript.attributes[i].name, oldScript.attributes[i].value);
oldScript.parentElement.replaceChild(newScript, oldScript);
});
}
// test
setInterval(reloadScripts, 5000);
</script>
As far as I know, there's no other way to reset a script than completely remove the old one and create another one with the same attributes and content. Not even clone the node would reset the script, at least in Firefox.
You said you want to reset timers. Do you mean clearTimeout() and clearInterval()? The methods Window.prototype.setTimeout() and Window.prototype.setInterval() both return an ID wich is to pass to a subsequent call of clearTimeout(). Unfortunately there is no builtin to clear any active timer.
I've wrote some code to register all timer IDs. The simple TODO-task to implement a wrapper callback for setTimeout is open yet. The functionality isn't faulty, but excessive calls to setTimeout could mess up the array.
Be aware that extending prototypes of host objects can cause undefined behavior since exposing host prototypes and internal behavior is not part of specification of W3C. Browsers could change this future. The alternative is to put the code directly into window object, however, then it's not absolutely sure that other scripts will call this modified methods. Both decisions are not an optimal choice.
(function()
{ // missing in older browsers, e.g. IE<9
if(!Array.prototype.indexOf)
Object.defineProperty(Array.prototype, 'indexOf', {value: function(needle, fromIndex)
{ // TODO: assert fromIndex undefined or integer >-1
for(var i=fromIndex || 0; i < this.length && id !== window.setTimeout.allIds[i];) i++;
return i < this.length ? i : -1;
}});
if(!Array.prototype.remove)
Object.defineProperty(Array.prototype, 'remove', { value: function(needle)
{ var i = this.indexOf(needle);
return -1 === i ? void(0) : this.splice(i, 1)[0];
}});
// Warning: Extensions to prototypes of host objects like Window can cause errors
// since the expose and behavior of host prototypes are not obligatory in
// W3C specs.
// You can extend a specific window/frame itself, however, other scripts
// could get around when they call window.prototype's methods directly.
try
{
var
oldST = setTimeout,
oldSI = setInterval,
oldCT = clearTimeout,
oldCI = clearInterval
;
Object.defineProperties(Window.prototype,
{
// TODO: write a wrapper that removes the ID from the list when callback is executed
'setTimeout':
{ value: function(callback, delay)
{
return window.setTimeout.allIds[window.setTimeout.allIds.length]
= window.setTimeout.oldFunction.call(this, callback, delay);
}
},
'setInterval':
{ value: function(callback, interval)
{
return window.setInterval.allIds[this.setInterval.allIds.length]
= window.setInterval.oldFunction.call(this, callback, interval);
}
},
'clearTimeout':
{ value: function(id)
{ debugger;
window.clearTimeout.oldFunction.call(this, id);
window.setTimeout.allIds.remove(id);
}
},
'clearInterval':
{ value: function(id)
{
window.clearInterval.oldFunction.call(this, id);
window.setInterval.allIds.remove(id);
}
},
'clearTimeoutAll' : { value: function() { while(this.setTimeout .allIds.length) this.clearTimeout (this.setTimeout .allIds[0]); } },
'clearIntervalAll': { value: function() { while(this.setInterval.allIds.length) this.clearInterval(this.setInterval.allIds[0]); } },
'clearAllTimers' : { value: function() { this.clearIntervalAll(); this.clearTimeoutAll(); } }
});
window.setTimeout .allIds = [];
window.setInterval .allIds = [];
window.setTimeout .oldFunction = oldST;
window.setInterval .oldFunction = oldSI;
window.clearTimeout .oldFunction = oldCT;
window.clearInterval.oldFunction = oldCI;
}
catch(e){ console.log('Something went wrong while extending host object Window.prototype.\n', e); }
})();
This puts a wrapper method around each of the native methods. It will call the native functions and track the returned IDs in an array in the Function objects of the methods. Remember to implement the TODOs.

Categories

Resources