Placing timer ID into its function - javascript

I would like to put timer ID, returned by setInterval(), into its function:
delay_timeout = setInterval(function () {
test_delay(data['time'], delay_timeout);
}, 1000);
Is it possible? To my mind delay_timeout doesn't have a value at this point...
I don't want to save delay_timeout globally for using later in timer's function to stop it. Several timers may work at the same time.
UPDATE:
Code is not global, it is located here:
socket.on('test_delay', function (data) {
...
});
The point is not to make delay_timeout global and be able to kill timer by some condition within its callback function.

your code works fine if you put the setTimeout call in it's own function like this:
function setTimer(){
var timeId = setTimeout(function(){ console.log(timeId); }, 1);
}
for(var i = 0; i < 10; i ++){
setTimer();
}
here's a fiddle
The delay_timeout variable is available to your callback as it is in the same enclosure.
So long as you are not in the same context and rerun your setTimeout before it has triggered the callback you will be fine. so this won't work:
var timeId;
for(var i = 0; i < 10; i ++){
timeId = setTimeout(function(){ console.log(timeId); }, 1);
}
(see the second half of the fiddle...)

Related

Clearing an anonymous setInterval

How do you clear an anonymous setInterval function, like the following, so that i stops incrementing?
var i = 0;
setInterval(function(){
console.log(i);
i++;
}, 1000);
You need to store it in variable and then pass that variable in clearInterval.
var i = 0;
let variable = setInterval(function(){
i++;
console.log(i)
}, 1000);
//to clear interval after 5 seconds to check if it works
setTimeout(() => clearInterval(variable),5000)
This is highly NOT recommended!
Nevertheless, you can accomplish this by temporarily monkey-patching the window.setInterval function so you can capture the reference to the timer.
The following is a consolidated example, see further below for an implementation example.
// Declare a variable to store the interval
let rogueInterval = null;
//Override the setInterval method
const oldSetInterval = window.setInterval;
window.setInterval = (...args) => {
rogueInterval = oldSetInterval(...args);
}
var i = 0;
setInterval(function() {
console.log(i);
i++;
}, 1000);
// Reset it
window.setInterval = oldSetInterval;
// Will clear your rogueInterval after 5 seconds
setTimeout(() => {
clearInterval(rogueInterval);
}, 5000);
It sounds like you are including a script via script.src, therefore, you would need to put an inline script tag before to override the setInterval, and an inline script tag after to reset the setInterval:
<script> // JS that overrides the setInterval </script>
<script src="problem-file.js"></script>
<script> // JS that resets the setInterval </script>
Unfortunately, this approach assumes there is only one setInterval within the problem file, and the setInterval is not set asynchronously itself. If you absolutely needed to, you could leave the monkey-patch in place, and check the stringified version of the function being passed in, and if it matches some criteria, then capture it.

the counter is doubling

While working on my personal project, I've encountered a problem. The problem is every time my function loops the counter counts twice.
JavaScript Code:
function punch(){
var hit;
var h1=100;
h1-=1;
counter++;
hit=setInterval('punch()',2000);
}
What I wanted it to do is that every 2000 milliseconds the counter goes up 1.
In your original code every time the function punch is called is called again internally.
var counter = 0;
function punch(){
var h1=100;
h1-=1;
counter++;
}
var hit = setInterval(punch,2000);
window.setInterval(function(){
/// call your function here
}, 2000);
Call your function where commented and inside the function increment your counter.
var counter = 0;
var h1=100;
setInterval(punch,2000);
function punch(){
h1-=1;
counter++;
}
Here you go: http://jsfiddle.net/9JLdU/3/
<div id="counter"></div>
<script>
var hit;
var counter = 0;
window.punch = function()
{
var h1=100;
h1-=1;
counter++;
document.getElementById('counter').innerHTML=counter;
}
hit = setInterval('punch()',2000);
</script>
setInterval will cause the function to be called about every 2 seconds until cancelled. By including another call to setInterval within the function, another sequence of calls is established each time it is called, so eventually you'll have thousands of instances running, each incrementing the counter.
So either use one call to setInterval, or have the function call itself using setTimeout, which only runs once. Also, it's preferred to pass a function reference to setInterval and setTimeout as passing a string calls the Function constructor and is effectively a call to eval, which is needlessly expensive in terms of system resources.
var counter = 0;
function punch() {
// ...
counter++;
hit = setTimeout(punch, 2000);
}
punch();
or
var counter = 0;
function punch() {
// ...
counter++;
}
setInterval(punch, 2000);
The advantage of setTimeout is that you can easily vary the delay based on some other logic, or stop the sequence without cancelling the timeout.
Note that when doing:
hit = setInterval(...);
the value of hit is an index that can be used to cancel the interval, it is not the value returned by punch (which is undefined since there is no return statement).
The setInterval() method calls a function or evaluates an expression at specified intervals (in milliseconds).
The setInterval() method will continue calling the function until clearInterval() is called, or the window is closed.
Read about setInerval() Here
Syntax
setInterval(function,milliseconds)
Working Example here
<script>
var hit = 100;
counter = 0;
var myVar = setInterval(function() {punch()}, 1000);
function punch() {
hit--;
counter++;
}
</script>

Why setInterval wont stop?

Why console.log(1) gets executed here forever:
var interval = setInterval(function() {
if (true) {
clearInterval(interval);
console.log(1);
}
}, 100);
It depends on the scope within which you're executing this code.
If interval is unique within its scope — be it global or function scope — then this will work as expected.
If, however, you execute this code within a loop (for example), then you are overwriting interval with some new interval on each iteration, breaking your clearInterval call for all but the very last setInterval call:
for (var i = 0; i < 3; i++) {
var interval = setInterval(function() {
if (true) {
clearInterval(interval);
console.log(1);
}
}, 100);
}
// ^ will give you one single console log entry,
// and two more console log entries per second forever
It's seems that your variable interval is used somewhere again. If I run code you provided it works as expected. I guess user Lightness has given a great explaination of this, also he provided piece of code where "closure problem" is obvious (which caused you problem too). I just want to add extra information. If you want your code inside of loop + setInteval works aparat you can do the following:
for (var i = 0; i < 3; i++) {
var o = {
i: i,
interval: null,
timer: function() {
if (true) {
clearInterval(this.interval);
console.log(this.i);
}
}
};
o.interval = setInterval(o.timer.bind(o), 1000);
}
DEMO
I hope it will be useful for someone.

Why is this javascript not running as expected?

function animateGraph() {
var graph;
for(i=0; i<10; i++)
{
var start = new Date();
while((new Date()) - start <= 500) {/*wait*/}
document.getElementById("timeMark").innerHTML = phoneX[i].epoch;
}
}
The loop works. The wait works. But the document.getElement is not showing up until the last item in the array...why?
Using setTimeout will allow the code to run and not lock up the page. This will allow it to run the code and will not effect other elements on the page.
var cnt = 0;
(function animateGraph() {
document.getElementById("timeMark").innerHTML = phoneX[cnt].epoch;
cnt++;
if (cnt<10){
window.setTimeout(animateGraph,500);
}
})();
The while loop, waiting for a datetime, is not a good way to wait - it just blocks execution. It keeps the browser (including UI, and its updating) frozen until the script finishes. After that, the window is repainted according to the DOM.
Use window.setTimeout() instead:
function animateGraph(phoneX) {
var el = document.getElementById("timeMark")
var i = 0;
(function nextStep() {
if (i < phoneX.length )
el.innerHTML = phoneX[i].epoch;
i++;
if (i < phoneX.length )
window.setTimeout(nextStep, 500);
})();
}
Please note that this runs asynchronous, i.e. the function animateGraph will return before all phoneXes are shown.
Use setTimeout instead of a while loop.
https://developer.mozilla.org/en/DOM/window.setTimeout
Also try something like this.
Javascript setTimeout function
The following snippet uses a helper function to create the timers. This helper function accepts a loop counter argument i and calls itself at the end of the timer handler for the next iteration.
function animateGraph() {
var graph;
setTimeMarkDelayed(0);
function setTimeMarkDelayed(i) {
setTimeout(function() {
document.getElementById("timeMark").innerHTML = phoneX[i].epoch;
if (i < 10) {
setTimeMarkDelayed(++i);
}
}, 3000);
}
}
You actually need some sort of helper function, otherwise you'll end up overwriting the value of i in your for loop in every iteration and by the time your timers run out, i will already be 9 and all handlers will act on the last element in phoneX. By passing i as an argument to the helper function, the value is stored in the local scope of that function and won't get overwritten.
Or you could use setInterval like Radu suggested, both approaches will work.

setTimeout and anonymous function problem

This is my code, SetOpacity get invoked with wrong values, why?
function SetOpacity(eID, opacity){
eID.style.opacity = opacity / 100;
eID.style.filter = 'alpha(opacity=' + opacity + ')';
}
function fade(eID, startOpacity, endOpacity){
var timer = 0;
if (startOpacity < endOpacity) {
for (var i = startOpacity; i <= endOpacity; i++) {
setTimeout(function() {SetOpacity(eID, i);}, timer * 30);
timer++;
}
}
}
This should work:
for (var i = startOpacity; i <= endOpacity; i++) {
(function(opacity) {
setTimeout(function() {SetOpacity(eID, opacity);}, timer * 30);
})(i);
timer++;
}
This works as follows:
inside the loop you create an anonymous function (function(...){...}) and immediately call it with a parameter (that's why there are parentheses around function(){}, so you can call it by adding () at the end and passing parameters)
parameters passed to this anonymous function (in this case i, which is opacity inside the function) are local to this anonymous function, so they don't change during the next iterations of the loop, and you can safely pass them to another anonymous function (the first parameter in setTimeout)
Your original version didn't work because:
your function that is passed to setTimeout holds a reference to the variable i (not the value of it), and it resolves its value only when this function is called, which is not at the time of adding it to setTimeout
the value of this variable gets changed in the loop, and before even the first setTimeout executes, i will have reached endOpacity (the last value from the for loop)
Unfortunately JavaScript only has function scope, so it won't work if you create the variable inside the loop and assign a new actual value, because whenever there is some var inside a function, those variables are created at the time of function execution (and are undefined by default). The only (easy) way to create new scope is to create a function (which may be anonymous) and create new variables inside it (parameters are variables too).
This is a closure issue. By the time you run the function, i is already at endOpacity. This will work, by creating another closure:
function SetOpacityTimeout(eID, opacity, timer){
setTimeout(function() {SetOpacity(eID, opacity);}, timer * 30);
}
function fade(eID, startOpacity, endOpacity){
var timer = 0;
if (startOpacity < endOpacity) {
for (var i = startOpacity; i <= endOpacity; i++) {
SetOpacityTimeout(eID,i,timer);
timer++;
}
}
}
Kobi has the right idea on the problem. I suggest you use an interval instead, though.
Here's an example: (Your SetOpacity function remains the same, I left it out here.)
function fade(eID, startOpacity, endOpacity){
var opacity = startOpacity;
SetOpacity(eID, opacity);
var interval = window.setInterval(function(){
opacity++;
SetOpacity(eID, opacity);
// Stop the interval when done
if (opacity === endOpacity)
window.clearInterval(interval);
}, 30);
}
This is am example I used with jquery. "menuitem" is the itemclass and jquery checks the "recentlyOut" class to see if it needs to slide back up.
The code speaks for itself.
$(".menuitem").mouseenter(
function(){
$(this).addClass("over").removeClass("out").removeClass("recentlyOut");
$(this).children(".sub").slideDown();
});
$(".menuitem").mouseleave(
function(){
$(this).addClass("out").addClass("recentlyOut").removeClass("over");
setTimeout(function()
{
var bool = $(".recentlyOut").hasClass("over");
if (!bool)
{
$(".recentlyOut").removeClass("recentlyOut").children(".sub").slideUp();
}
}
, 400);
}
);

Categories