Look at the following code:
var timer=setTimeout(function(){increase();}, 8);
This setTimeout function will be executed immediately, but I want
it to execute later. Why?
Example:
function increase(i)
{
i++; if(i==100) return;
var timer=setTimeout(function(){increase(i);}, 8);
}
Now, I need to stop and exit this function within another function when certain thing happen:
if (a==b) clearTimeout(timer);
The thing that bothers me is that variable timer is getting assigned, whenever
function increase runs, but it does not need to, and I believe it is bad practice. That is why I need to assign to it only once, before function run and execute it later when need arrives.
I hope you understood, and btw, those are just examples, not my code.
You can declare a variable outside of function the calls setTimeout, define the variable as setTimeout when the function is called; call clearTimeout() from another function with variable referencing setTimeout as parameter.
var timer = null, // declare `timer` variable
n = 0, // reference for `i` inside of `increase`
i = 0,
a = 50,
b = 50,
// pass `increase` to `t`, call `increase` at `setTimeout`
t = function(fn, i) {
// define timer
timer = setTimeout(function() {
fn(i)
}, 8)
};
function increase(i) {
console.log(i);
// set `n` to current value of `i` to access `i`:`n`
// to access `i` value outside of `t`, `increase` functions
n = i++;
if (i == 100) return;
t(increase, i); // call `t`
}
increase(i);
// do stuff outside of `t`, `increase`
setTimeout(function() {
// clear `timer` after `200ms` if `a == b`
if (a == b) {clearTimeout(timer)};
alert(n)
}, 200)
If you want the operation of one function to change the conditions of another, just declare a boolean variable within the scope of both functions and change it's value depending on a terminator function.
For example, take a look at this code:
var exit = false;
function increase(i) {
if(i==100 || exit) return;
setTimeout(function(){ increase(++i) }, 1000);
}
function terminator(a, b){
exit = (a==b);
}
increase(0);
Here, if terminator is ever called with a pair of equal arguments like:
setTimeout(function(){ terminator(true, 1) }, 5000) // timeout of 5 seconds means increase will run 5 times
the recursive call of setTimeout within the increase function will not be reached (after 5 seconds), as the function will return before reaching that line of code.
If terminator is never called, or called with unequal arguments like:
setTimeout(function(){ terminator(true, false) }, 5000) // using setTimeout here is just arbitrary, for consistency's sake
increase will only time out once it's completed 100 recursions (in other words, after 100 seconds have elapsed)
Hope this helps!
Because the delay in setTimeout takes millisecond as time unit, so in your code, you set your function to be executed after 8ms, which feels like immediately.
function increase(i, boolean) {
i++;
if (i == 100) return;
if (boolean) {
var timer = setTimeout(function() {
increase(i, true);
console.log(i);
}, 8);
}
}
increase(1,true);
What about you put in some extra argument
to the function?
Related
This is what my code looks like:
var fnInterval = setInterval(function() {
let b = true
if (b) {
console.log("hi")
} else {
console.log("bye")
}
b = !b
}, 1000);
clearTimeout(fnInterval, 10000)
I am a newbie to JavaScript and my aim here is to console log a message every 1 second for a total duration of 10 seconds, but during each interval I want my message to toggle its value between "hi" and "bye" . How can I do it? (as of now it displays the value for the default boolean and doesn't change later)
Move the flag variable out of the function:
let b = true;
const fnInterval = setInterval(function() {
if (b) {
console.log("hi");
} else {
console.log("bye");
}
b = !b
}, 1000);
To stop the timer after 10000 milliseconds, wrap the call to clearInterval in a setTimeout:
setTimeout(() => clearInterval(fnInterval), 10000);
Meanwhile, note that the return value of setInterval is not a function but a number, so it may be misleading to call it fnInterval.
First of all, declare let b = true outside of the callback function. It's re-initialized on each call otherwise.
Secondly, the 10000 in clearTimeout(fnInterval, 10000) isn't a valid parameter. clearTimeout(timeoutId) accepts only the first parameter and clears the timeout passed in immediately. You'd need a setTimeout to trigger this after 10 seconds, if that's your goal. But that causes a race condition between the two timeouts -- imprecision can mean you'll miss some of the logs or wind up with extra logs.
Using a counter is one solution, as other answers show, but usually when I'm using complex timing with setInterval that requires clearing it after some number of iterations, I refactor to a generic promisified sleep function based on setTimeout. This keeps the calling code much cleaner (no callbacks) and avoids messing with clearTimeout.
Instead of a boolean to flip a flag back and forth between two messages, a better solution is to use an array and modulus the current index by the messages array length. This makes it much easier to add more items to cycle through and the code is easier to understand since the state is implicit in the counter.
const sleep = ms => new Promise(res => setInterval(res, ms));
(async () => {
const messages = ["hi", "bye"];
for (let i = 0; i < 10; i++) {
console.log(messages[i%messages.length]);
await sleep(1000);
}
})();
setInterval() is stopped by clearInterval() not clearTimeout(). Details are commented in code below.
// Define a counter
let i = 0;
// Define interval function
const fnCount = setInterval(fnSwitch, 1000);
function fnSwitch() {
// Increment counter
i++;
// if counter / 2 is 0 log 'HI'
if (i % 2 === 0) {
console.log(i + ' HI');
// Otherwise log 'BYE'
} else {
console.log(i + ' BYE');
}
// If counter is 10 or greater run fnStop()
if (i >= 10) {
fnStop();
}
};
function fnStop() {
// Stop the interval function fnCount()
clearInterval(fnCount);
};
After solving a problem about how to display data of an array each XXX seconds with setIterval funcion with the code:
var iterations = 0,
data = ['a','bbbbbbbb','c'],
interval = setInterval(foo, 4000);
function foo() {
console.log(data[iterations]);
iterations++;
if (iterations >= 4){
clearInterval(interval);
}else if(iterations == 1){
//we want to make time longer in this iteration.
clearInterval(interval);
interval = setInterval(foo, 8000);
}
}
I want to clean things and capsulate all this into a function. Problem is, when I insert global vars into a function, foo says all vars are unknown so I've to pass them as attrs. Also, I've changed interval to this in order to detect it, but now it does not work. Displays all data at the same time, seems to ignore setIterval
runTest();
function runTest(){
var iterations = 0,
data = [1,2,3,4,5],
maxIterations = 5,
interval = setInterval(foo(iterations,data,maxIterations),4000);
}
function foo(iterations,data,maxIterations){
var sentence = data[iterations];
var div = d3.select('div').append('p').text(sentence);
iterations++;
if (iterations >= maxIterations){
clearInterval(this);
}else{
clearInterval(this);
interval = setInterval(foo(iterations,data,maxIterations),4000);
}
}
If I set a breakpoint on Chrome debugger flow app seems to be right.
Do you know where the problem is? Thanks!
setInterval needs function as parameter (or a function reference). However, you are passing the result of the function call to foo.
You probably want something like
var iterations = 0,
data = [1,2,3,4,5],
maxIterations = 5,
interval = setInterval(function() { foo(iterations,data,maxIterations); }, 4000);
Here you are passing a function which in turn calls foo with all necessary parameters.
The problem is that you declare the variable 'interval' for the first time inside a function. The variable is not global but localized to that function.
it will work if you first declare all the variables you want to have global, outside any functions.
var iterations, maxIterations, data, interval;
runTest();
function runTest(){
iterations = 0,
data = [1,2,3,4,5],
maxIterations = 5,
interval = setInterval(foo(iterations,data,maxIterations),4000);
}
Be aware that the function foo(), might not change the values of the global variables since you choose the same names for the local variables.
I am trying to undrstand the code
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
from here http://bonsaiden.github.com/JavaScript-Garden/#function.closures
I understood this method :
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
Can anyone please help me by explaining the first one?
I will try to explain how I understands the first one,
first i is 0,
setTimeout is called,
self calling function "function(e)" is called with i=0,
Im stuck!! what happens when this function returns a function?
All the first one does is return a function that will be called after the timeout happens.
The purpose of it is to create a sub-scope for each iteration of the for loop so that the incrementing i isn't overridden with each iteration.
More explanation:
Lets take this apart into two different pieces:
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
This is the first piece:
for(var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i); //9-9
},1000);
}
Now, when you run this loop, you will always get console.log()'s that contain 9 instead of 0 to 9. This is because each setTimeout is using the same reference to i.
If you wrap the setTimeout part of that in an anonymous function, it creates a scope for each iteration allowing each setTimeout to have it's own i value.
for(var i = 0; i < 10; i++) {
setTimeout((function(i) {
return function() {
console.log(i); // 0-9
}
})(i), 1000)
}
The outer function inside the setTimeout gets executed immediately with an i of 0 for first iteration, 1 for second, etc. That function then in turn returns a function which is the function that setTimeout uses. A function is being generated and returned for each iteration of the loop using a different value for i.
Both end up with the same result: a setTimeout is called with a function to invoke, which writes a number from 0 to 9 on the console. Both use nested functions to get the current value of i into a closure so you don't end up logging 10 9's.
The first code chooses to have a function returning the function that setTimeout will call. The second changes the nesting order so that the closed-over function invokes setTimeout itself. The net effect is the same.
Other than stylistic reasons and personal choice, I don't see a reason to choose one over the other.
"Can you please check the updated question specifying where im getting confused"
OK, here's the long explanation. Remember that the first parameter to setTimeout() needs to be a reference to the function that you want executed after the specified delay. The simplest case is to just name a function defined elsewhere:
function someFunc() {
console.log("In someFunc");
}
setTimeout(someFunc, 100);
Note there are no parentheses on someFunc when passing it as a parameter to setTimeout because a reference to the function itself is required. Contrast with:
setTimeout(someFunc(), 100); // won't work for someFunc() as defined above
With parenthese it calls someFunc() and passes its return value to setTimeout. But my definition of someFunc() above doesn't explictly return a value, so it implicitly returns undefined - which is like saying setTimeout(undefined, 100).
But it would work if changed someFunc() to return a function instead of returning undefined:
function someFunc() {
return function() {
console.log("In the function returned from someFunc");
};
}
So now (at last) we come to the code from your question:
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
Instead of referencing a function by name and calling it as someFunc(i) it defines an anonymous function and calls it immediately as (function(e) {})(i). That anonymous function returns another function and it is that returned function that becomes the actual parameter to setTimeout(). When the time is up it is that returned function that will be executed. Because the (inner) function being returned is defined in the scope of the (outer) anonymous function it has access to the e parameter.
99 times out of 100, this works perfectly:
function a(){
setInterval("b()",1000);
updateText("still working");
}
function b(){
timer++;
updateText(timer);
}
Occasionally the first loop waits for 20 seconds to 2 minutes. Thereafter it runs perfectly. I know the timer can pause on Android phones (when the soft keyboard is shown). Are there other conditions that might delay setInterval?
Firstly, it is strongly advised you provide a callback(function) as the first argument and not a string, because that string is evaluated in the global scope and we all know that bad things happen when we use eval in js (related eval post : When is JavaScript's eval() not evil?).
So, your
setInterval("b()", 1000);
should be rewritten as :
setInterval(b, 1000);
or:
setInterval(function() { b(); }, 1000);
I also recommend you use setTimeout to simulate a setInterval.
The main downfall of the setInterval function is that it executes a block of code every n milliseconds, regardless of the execution of the previous block of code.
So if for some reason a setInterval callback takes longer to execute than the delay provided, it will cause some stack overflows.
Let's take the following code for example :
function foo() {
// this takes about 2 seconds to execute
// .. code here
}
setInterval(foo, 1000);
This will actually freeze the browser because it will execute foo for an (almost) infinite number of times but it will never finish it.
The solution in this kind of case is to emulate the setInterval with setTimeout, in order to ensure that the callback has finished to execute before calling it again:
function foo() {
// this takes about 2 seconds to execute
// .. code here
}
function newSetInterval(callback, duration, callbackArguments) {
callback.apply(this, callbackArguments);
var args = arguments,
scope = this;
setTimeout(function() {
newSetInterval.apply(scope, args);
}, duration);
}
newSetInterval(foo, 1000);
Now, foo is called again only after the previous instance has finished the code execution.
I would apply the same thing to your code, in order to let the browser decide when it can execute the code, and not to force it to execute the block of code weather it's busy at that moment or not:
function a() {
newSetInterval(b, 1000);
updateText("still working");
}
function b() {
timer++;
updateText(timer);
}
function newSetInterval(callback, duration, callbackArguments) {
callback.apply(this, callbackArguments);
var args = arguments,
scope=this;
setTimeout(function() {
newSetInterval.apply(scope, args);
}, duration);
}
If you're interested, I've rewritten the setInterval and clearInterval functions in order to use them anywhere, without taking care of stack overflows :
function setInterval(f, time) {
setInterval.ids = setInterval.ids || {};
setInterval.idCount = setInterval.idCount || 0;
var that = this,
id = setInterval.idCount++,
// to prevent firefox bug that adds an extra element to the arguments
l = arguments.length - 2;
(function theFn() {
// to prevent firefox bug that adds an extra element to the arguments
var args = [].slice.call(arguments, 0, l);
f.apply(this, args);
setInterval.ids[id] = setTimeout.apply(this, [theFn, time].concat(args));
}).apply(that, [].slice.call(arguments, 2, arguments.length));
return id;
}
function clearInterval(id) {
if(!setInterval.ids || !setInterval.ids[id]) {
return false;
}
clearTimeout(setInterval.ids[id]);
return true;
}
try this,
setInterval(b, 1000);
or
setInterval(function(){
timer++;
updateText(timer);
}, 1000);
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);
}
);