I am trying to know what is the value of the variable when setTimeout get called and assigned to it. Unexpected value get logged, so being curious how this happening.
I tried to look some related question on stack and also look out on web but didn't get the exact answer.
Update - I am seeking help to understand how the unique id which assigne d to variable get calculated ?
let a = setTimeout( () => {
console.log('something will happen')
}, 2000)
let b = setTimeout( () => {
console.log('something will happen')
}, 3000)
let c = setTimeout( () => {
console.log('something will happen')
}, 4000)
console.log(a, b, c)
There is documentation on what type of values to expect.
Quote:
The returned timeoutID is a positive integer value which identifies the timer created by the call to setTimeout(); this value can be passed to clearTimeout() to cancel the timeout.
It may be helpful to be aware that setTimeout() and setInterval() share the same pool of IDs, and that clearTimeout() and clearInterval() can technically be used interchangeably. For clarity, however, you should try to always match them to avoid confusion when maintaining your code.
It is guaranteed that a timeout ID will never be reused by a subsequent call to setTimeout() or setInterval() on the same object (a window or a worker). However, different objects use separate pools of IDs.
When you call setTimeout or setInterval the uniq id of particular timer is returned.
Usually it is just a number, incremented with every new timer and it is guaranteed that this number will never be reused.
You can use it only for cancelling timeouts or intervals with corresponding function clearInterval or clearTimeout.
More about returned value here.
Related
I want to stop this requestAnimationFrame after 1 second:
rAF = requestAnimationFrame(draw);
So I use setTimeout.
When I wrap the cancelAnimationFrame(rAF) in an arrow function, it works fine:
setTimeout(() => cancelAnimationFrame(rAF), 1000);
But, when I use cancelAnimationFrame as the function itself and pass rAF as the third argument to setTimeout, it doesn’t work:
setTimeout(cancelAnimationFrame, 1000, rAF);
I thought that I didn’t know the exact syntax for setTimeout at first. But, I think the syntax isn’t wrong as this code works fine:
setTimeout(alert, 1000, "Hello");
Why doesn’t it work?
The difference is the moment in time at which rAF is evaluated.
Explanation
Presumably, your code looks something like this:
let rAF;
const draw = () => {
// Do some work.
rAF = requestAnimationFrame(draw);
};
rAF = requestAnimationFrame(draw);
When you do this:
setTimeout(cancelAnimationFrame, 1000, rAF);
you pass three values to setTimeout: a function, a number, and another number.
When this statement is executed the call is evaluated, meaning that first, the setTimeout identifier is resolved to a function reference.
Second, the three arguments are evaluated: the cancelAnimationFrame identifier is resolved to a function reference, then the number literal is a number primitive, then the rAF identifier is resolved to another number primitive.
Then, the call is performed.
That’s all that setTimeout sees.
In JavaScript, you cannot pass a reference to a number, like you can in C, for example.
Let’s assume rAF is initially 1.
Over the course of one second, rAF has been repeatedly incremented and eventually reaches the value 61 or so.
Since you register the setTimeout at the start, the statement
setTimeout(cancelAnimationFrame, 1000, rAF);
is equivalent to
setTimeout(cancelAnimationFrame, 1000, 1);
However, the statement
setTimeout(() => cancelAnimationFrame(rAF), 1000);
is not equivalent to
setTimeout(() => cancelAnimationFrame(1), 1000);
The function bodies are only evaluated when they are called.
This means, JS doesn’t “peek inside the functions” and attempt to evaluate variables.
That statement essentially means “call some function with some other function and the number 1000 as arguments”.
When the one second is over and it’s time to cancel the animation frame, setTimeout executes its callback.
If the callback is () => cancelAnimationFrame(rAF), then it’s executed, so the function body is evaluated: cancelAnimationFrame(rAF) is equivalent to cancelAnimationFrame(61).
However, in the non-working case, cancelAnimationFrame stays the same, the argument 1 (equivalent to rAF at the time setTimeout was originally called) stays the same.
You can’t cancel frame 1 when you’re already at frame 61.
And setTimeout(alert, 1000, "Hello"); works, of course, because "Hello" is static, is only evaluated once, never changes.
Related
Here’s a more general situation where this behavior can be examined:
let greeting = "Hello";
const greet = (theGreeting) => console.log(`${theGreeting}, world!`);
const boundGreeting = greet.bind(null, greeting);
greeting = "Goodbye";
boundGreeting(); // Logs "Hello, world!".
greet(greeting); // Logs "Goodbye, world!".
bind passes the 2nd parameter (greeting) as the 1st argument to greet (ignore the null).
This is much like using setTimeout(greet, 0, greeting);, except this is unrelated to timeouts and we call (the bound) greet ourselves.
You could pass something like a reference, i.e. an object, to make it work, if you had a function that also accepts an object:
const timing = {
rAF: null
},
cancelTiming = ({ rAF }) => cancelAnimationFrame(rAF),
draw = () => {
// Do some work.
timing.rAF = requestAnimationFrame(draw);
};
timing.rAF = requestAnimationFrame(draw);
setTimeout(cancelTiming, 1000, timing);
This works because when passing timing, the value being passed is a reference.
Mutating it, e.g. by updating the rAF property, is visible everywhere where this reference is visible.
But this makes programming quite cumbersome.
This is tangentially related to Is JavaScript a pass-by-reference or pass-by-value language?.
Alternative
There’s an alternative to using setTimeout.
When requestAnimationFrame calls its callback function, it passes a DOMHighResTimeStamp, similar to what performance.now returns.
So you could make the check in your draw function:
const timeoutTimestamp = performance.now() + 1000,
draw = (now) => {
// Do some work.
if(now < timeoutTimestamp){
requestAnimationFrame(draw);
}
};
requestAnimationFrame(draw);
Related: Stop requestAnimationFrame after a couple of seconds.
I have the following Javascript code attached to an HTML document with some CSS styling.
It is simply a box on the screen that changes the style background property colour according to those seen in the array. The id of "colour-changer" is used to access the HTML document and changes it each time the array is iterated.
The function declaration changeColour(); is used to make this happen by using colours.length to count its way through the array and then puts the array value into the HTML, which changes its background colour every 3 seconds.
Once it comes to the end of the array the counter is reset to 0, and it continues around again.
The setInterval method invokes the callback method changeColour() every 3 seconds to make this happen.
In order to stop the cycle, an onclick is event is added which invokes a clearInterval() method to print out "Timer stopped" inside the box. In order to do this the setInterval() method had to be stored in variable myTimer.
See the code below. It all works fine but this is not my real problem.
let colourChanger = document.getElementById ("colour-changer");
let colours = ["red","blue","green","pink"];
let counter = 0;
function changeColour(){
if (counter >= colours.length){
counter = 0;
}
colourChanger.style.background = colours[counter];
counter++;
}
let myTimer = setInterval(changeColour, 3000);
colourChanger.onclick = function (){
clearInterval(myTimer);
colourChanger.innerHTML = "Timer stopped";
}
What I cannot understand is the line let myTimer = setInterval(changeColour, 3000);
From my understanding if you store a function inside of a variable, it will NOT execute unless called separately. It will just sit there stored in the variable myTimer.
There is no setInterval(); call made outside of the variable anywhere.
MY QUESTION:
How then is this method invoked since it is simply stored inside of the variable myTimer?
No, your understanding is wrong.
It executes setInterval(changeColour, 3000); and stores this particular interval reference ID to myTimer to later be used in clearInterval(myTimer)
var myTimer = setInterval(() => console.log('ping'), 3000);
setTimeout(() => clearInterval(myTimer), 10000);
console.log('myTimer value: ', myTimer);
In order to do this the setInterval() method had to be stored in variable myTimer.
No, the return value of setInterval is stored in myTimer by that code. setInterval is called (it has (/*...*/) after it, which calls it).
From my understanding if you store a function inside of a variable, it will NOT execute unless called separately.
That's correct, but that's not what that line of code is doing. This code:
let myTimer = setInterval(changeColour, 3000);
calls setInterval and stores its return value in myTimer, exactly the way:
let x = example();
calls example and stores its return value in x.
It's the changeColour function that is only referenced, not called, by that code (there's no (/*...*/) after it). Doing that passes changeColour into setInterval, so that setInterval knows what function to call every three seconds or so.
So in that code, setInterval is called, and changeColour is just referenced (it's called later by the timer mechanism).
setInterval() function will return IntervalID, It will set execution interval for given milliseconds.
In your case once this line is executed it will start executing changeColour function every 3000ms / 3 seconds and return IntervalID.
Now this IntervalID is used to stop executing your function every 3 seconds in the future.
to stop executing you can use clearInterval(IntervalID).
if any doubts please comment.
I noticed some weird behavior in constructor functions. This code loops infinitely and I don't know why.
function thing(){
this.start=function (){console.log(this.msg)};
this.msg="Starting...";
setInterval(() => {this.start()},1000)
}
<button onclick="new thing()">Create a new thing!</button>
I've searched about this but I found nothing that explains this. Please someone help me and answer why this happens.
Thanks.
Are you sure you wanted to use setInterval not setTimeout? The former will callthis.start every 1 second, while the latter will be called once after 1 second,
Please take a look at the below links explaining both functions:
setInterval
https://developer.mozilla.org/pl/docs/Web/API/Window/setInterval
setTimeout
https://developer.mozilla.org/pl/docs/Web/API/Window/setTimeout
Correct snippet should be:
function thing(){
this.start=function (){console.log(this.msg)};
this.msg="Starting...";
setTimeout(this.start(), 1000)
}
<button onclick="new thing()">Create a new thing!</button>
It does so because setInterval() sets up an... You've guessed it! Interval, which continues to execute it's contents at the interval you've set (1000ms in this example).
setTimeout() will delay execution once at the set delay.
Yes setInterval will call after 1 sec so if you want to stop that you need to use clearInterval.
From the W3School documentation:
Definition and Usage
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.
The ID value returned by setInterval() is used as the parameter for the clearInterval() method.
Tip: 1000 ms = 1 second.
Tip: To execute a function only once, after a specified number of milliseconds, use the setTimeout() method.
So you may want to do:
function thing(){
this.start=function (){console.log(this.msg)};
this.msg="Starting...";
setTimeout(() => {this.start()}, 1000)
}
<button onclick="new thing()">Create a new thing!</button>
setInterval is actually supposed to start an interval (timed loop).
Also, you are executing the function rather than binding it :) Common mistake.
onclick="new thing()"
https://developer.mozilla.org/de/docs/Web/API/WindowTimers/setInterval
Your code can be done much simpler:
const start = () => setTimeout( () => console.log(200), 1000)
<button onclick="start">Start!</button>
In most cases, you will not need objects in JS. Functions as first-class objects are a very powerful feature of JS.
I know there is an Answer for this But!! All The Answers covered with only one setTimeout in the loop this Question Looks relevant to me How do I add a delay in a JavaScript loop?
But in my Scenario I Have two setTimeout in the Script, How can this be implemented correctly with timing !! The Program works correctly but the timing what I want is not correct !!!
function clickDate(i)
{
setTimeout((function(){
alert("4");
})(),2000);
}
function clickButton(i)
{
setTimeout((function(){
alert("5");
})(),4000);
}
function doEverything(i)
{
clickDate(i);
clickButton(i);
}
for(var i = 0; i < 4; i++)
{
doEverything(i);
}
You're immediately calling the function when you pass it to setTImeout. Remove the extra parenthesis.
function clickDate(i)
{
setTimeout(function(){
alert("4");
},2000);
}
function clickButton(i)
{
setTimeout(function(){
alert("5");
},4000);
}
function doEverything(i)
{
clickDate(i);
clickButton(i);
}
for(var i = 0; i < 4; i++)
{
doEverything(i);
}
EDIT
It's a little unclear what exactly it is you want your code to do seeing as you're passing i into your function I assume you want to use it somehow. Currently you're creating timeouts that will all launch at once. You'll need to stagger the delay times if you want them to launch in sequence. The code below logs a "4" every 2 seconds and a "5" every "4" seconds by multiplying the delay time by i+1.
// Currently this code displays a 4 every 2 seconds and a 5 every 4 seconds
function clickDate(i)
{
setTimeout(function(){
console.log("4");
},2000 * (i+1));
}
function clickButton(i)
{
setTimeout(function(){
console.log("5");
},4000 * (i+1));
}
function doEverything(i)
{
clickDate(i);
clickButton(i);
}
for(var i = 0; i < 4; i++)
{
doEverything(i);
}
Hello I think you havent read documentation about javascript.
It's asynchronous and it will not wait for the event and continue the process. I will give the answer but I highly recommend to read about Javascript it's good for you only here you will get timing problem because your both the function will be called at the same time. Let me give you the example.
function clickDate(i,callback)
{
setTimeout(function(){
alert("4");
callback();//this will call anonymous function in doEverything
},2000);
}
function clickButton(i)
{
setTimeout(function(){
alert("5");
},4000);
}
function doEverything(i)
{
console.log("In loop index is " , i);
clickDate(i,function(){
clickButton(i);
});
//look closely here I have passed the function in changeData and will call that funtion from clickDate
console.log("In loop terminating index is " , i);
}
for(var i = 0; i < 4; i++)
{
doEverything(i);
}
So here console log will make you clear about asynchronous
functionality. You will see that for loop terminates as it continues
it's work and easily completed in 2 seconds so before your first alert
for loop will complete it's iteration.
Hopefully this will help.
you are calling the callback immediately by adding () to the end of function .
you need to pass the callback with timeout and it will be call for you
setTimeout(function(){
alert('hello');
} , 3000);
function functionName() {
setTimeout(function(){ //Your Code }, 3000);
}
Try this one.
Your approach to mocking asynchronous behavior in JavaScript with setTimeout is a relatively common practice. However, providing each function with its own invocation of setTimeout is an anti-pattern that is working against you simply due to the asynchronous nature of JavaScript itself. setTimeout may seem like it's forcing JS to behave in a synchronous way, thus producing the 4 4 4 4 then 5 5 you are seeing on alert with iteration of the for loop. In reality, JS is still behaving asynchronously, but because you've invoked multiple setTimeout instances with callbacks that are defined as anonymous functions and scoped within their own respective function as an enclosure; you are encapsulating control of JS async behavior away from yourself which is forcing the setTimeout's to run in a strictly synchronous manner.
As an alternative approach to dealing with callback's when using setTimeout, first create a method that provides the timing delay you want to occur. Example:
// timer gives us an empty named "placeholder" we can use later
// to reference the setTimeout instance. This is important because
// remember, JS is async. As such, setTimeout can still have methods
// conditionally set to work against it.
let timer
// "delayHandler", defined below, is a function expression which when
// invoked, sets the setTimeout function to the empty "timer" variable
// we defined previously. Setting the callback function as the function
// expression used to encapsulate setTimeout provides extendable control
// for us later on however we may need it. The "n" argument isn't necessary,
// but does provide a nice way in which to set the delay time programmatically
// rather than hard-coding the delay in ms directly in the setTimeout function
// itself.
const delayHandler = n => timer = setTimeout(delayHandler, n)
Then, define the methods intended as handlers for events. As a side-note, to help keep your JS code from getting messy quickly, wrap your event handler methods within one primary parent function. One (old school) way to do this would be to utilize the JS Revealing Module Pattern. Example:
const ParentFunc = step => {
// "Private" function expression for click button event handler.
// Takes only one argument, "step", a.k.a the index
// value provided later in our for loop. Since we want "clickButton"
// to act as the callback to "clickDate", we add the "delayHandler"
// method we created previously in this function expression.
// Doing so ensures that during the for loop, "clickDate" is invoked
// when after, internally, the "clickButton" method is fired as a
// callback. This process of "Bubbling" up from our callback to the parent
// function ensures the desired timing invocation of our "delayHandler"
// method. It's important to note that even though we are getting lost
// in a bit of "callback hell" here, because we globally referenced
// "delayHandler" to the empty "timer" variable we still have control
// over its conditional async behavior.
const clickButton = step => {
console.log(step)
delayHandler(8000)
}
// "Private" function expression for click date event handler
// that takes two arguments. The first is "step", a.k.a the index
// value provided later in our for loop. The second is "cb", a.k.a
// a reference to the function expression we defined above as the
// button click event handler method.
const clickDate = (step, cb) => {
console.log(step)
cb(delayHandler(8000))
}
// Return our "Private" methods as the default public return value
// of "ParentFunc"
return clickDate(step, clickButton(step))
}
Finally, create the for loop. Within the loop, invoke "ParentFunc". This starts the setTimeout instance and will run each time the loop is run. Example:
for(let i = 0; i < 4; i++) {
// Within the for loop, wrap "ParentFunc" in the conditional logic desired
// to stop the setTimeOut function from running further. i.e. if "i" is
// greater than or equal to 2. The time in ms the setTimeOut was set to run
// for will no longer hold true so long as the conditional we want defined
// ever returns true. To stop the setTimeOut method correctly, use the
// "clearTimeout" method; passing in "timer", a.k.a our variable reference
// to the setTimeOut instance, as the single argument needed to do so.
// Thus utilizing JavaScript's inherit async behavior in a "pseudo"
// synchronous way.
if(i >= 2) clearTimeout(timer)
ParentFunc(i)
}
As a final note, though instantiating setTimeOut is common practice in mocking asynchronous behavior, when dealing with initial invocation/execution and all subsequent lifecycle timing of the methods intended to act as the event handlers, defer to utilizing Promises. Using Promises to resolve your event handlers ensures the timing of method(s) execution is relative to the scenario in which they are invoked and is not restricted to the rigid timing behavior defined in something like the "setTimeOut" approach.
Why when calling fadeIn() onLoad does the browser run through the loop immediately. In other words there is an issue with either the setInterval or the Opacityto().
function Opacityto(elem,v){
elem.style.opacity = v/100;
elem.style.MozOpacity = v/100;
elem.style.KhtmlOpacity = v/100;
elem.style.filter=" alpha(opacity ="+v+")";}
function fadeIn(){
elem=document.getElementById('nav');
for (i=0;i==100;i++){
setInterval(Opacityto(elem,i),100);}
}
I think someone will tell me this can be done easiest with jQuery but I'm interested in doing it with javascript.
Thanks!HelP!
You've got several problems with your fadeIn() function:
A. Your for loop condition is i==100, which is not true on the first iteration and thus the body of the for loop will never be executed (the i++ won't ever happen). Perhaps you meant i<=100 or i<100 (depending on whether you want the loop to run 101 or 100 times)?
B. Your setInterval code has a syntax error EDIT: since you've updated your question to remove the quotes - setInterval expects either a string or a function reference/expression. So you need to either pass it the name of a function without parentheses and parameters, or a function expression like the anonymous function expression you can see in my code down below. in the way you try to build the string you are passing it. You've got this:
"Opacityto("+elem,i+")"
but you need this:
"Opacityto(elem," + i + ")"
The latter produces a string that, depending on i, looks like "Opacityto(elem,0)", i.e., it produces a valid piece of JavaScript that will call the Opacityto() function.
C. You probably want setTimeout() rather than setInterval(), because setInterval() will run your Opacityto() function every 100ms forever, while setTimeout() will run Opacityto() exactly once after the 100ms delay. Given that you are calling it in a loop I'm sure you don't really want to call setInterval() 100 times to cause your function Opacityto() to be run 100 times every 100ms forever.
D. Even fixing all of the above, your basic structure will not do what you want. When you call setInterval() or setTimeout() it does not pause execution of the current block of code. So the entire for loop will run and create all of your intervals/timeouts at once, and then when the 100ms is up they'll all be triggered more or less at once. If your intention is to gradually change the opacity with each change happening every 100ms then you need the following code (or some variation thereon):
function fadeIn(i){
// if called with no i parameter value initialise i
if (typeof i === "undefined") {
i = -1;
}
if (++i <= 100) {
Opacityto(document.getElementById('nav'), i);
setTimeout(function() { fadeIn(i); }, 100);
}
}
// kick it off:
fadeIn();
What the above does is defines fadeIn() and then calls it passing no parameter. The function checks if i is undefined and if so sets it to -1 (which is what happens if you call it without passing a parameter). Then it increments i and checks if the result is less than or equal to 100 and if so calls Opacityto() passing a reference to the element and i. Then it uses setTimeout() to call itself in 100ms time, passing the current i through. Because the setTimeout() is inside the if test, once i gets big enough the function stops setting timeouts and the whole process ends.
There are several other ways you could implement this, but that's just the first that happened as I started typing...
My guess is that there is a nasty comma inside the setInterval, messing the argument list:
"Opacityto("+elem,i+")"
^^^
here
You could try quoting the comma
+ "," +
but eval is evil so don't do that. The good way is to pass a real callback function:
function make_opacity_setter(elem, i){
return function(){
OpacityTo(elem, i);
};
}
...
setTimeout( make_opacity_setter(elem, i), 1000);
Do note that the intermediate function-making-function is needed to avoid the nasty interaction between closures and for-loops.
BTW, when you do
setInterval(func(), 1000)
you call func once yourself and then pass its return value to setInterval. since setInterval receives a junk value instead of a callback it won't work as you want to.