Javascript for loop with settimeout time change - javascript

So I'm trying to increase the time out in a for loop, I followed the answer here setTimeout with Loop in JavaScript but the duration of the setTimeout is just not working in milliseconds when I output the date to check the time difference.
Code:
for (var count = 0; count < 12; count++) {
(function(num){setTimeout(function() {
console.log("Count = " + num + " Time: " + 1000 * num + " Stamp: " + new Date());
}, 1000 * num)})(count);
}
As you can see in the console, it's still firing the console.log every 1000 instead of 1000 * count.
I hope the above makes sense and hopefully someone can explain this behaviour.

Is this what you are looking for? Increase the time for each iteration 1second * some count
let i = 0;
loop = () => {
setTimeout(() => {
console.log("Count = " + i + " Time: " + 1000 * i);
i++;
if (i < 10) {
loop();
}
}, i * 1000)
};
loop();
Here is a REPL, you can hit run
Explanation: Try running this code
for (var i = 1; i < 5; i++) {
setTimeout(() => console.log(i), 1000)
}
We would expect the output to be 1 2 3 4 but its actually 5 5 5 5, that's because js runtime engine does not wait for the settimeout to complete instead it passes it onto the web API and it is queued there, so the for loop executes first making i = 5 and at a later point of time the Web API's triggers the set time out function. You can learn about this in detail here.

The code is working as expected.
All the timers are all set almost at the same time as they are async. So, the timers are set and the code continues loading the other timers without waiting for the code to be executed.
The first timer will go off after 1 second.
The second timer will go off after 2 seconds. But two seconds after the timer was set, which is the same time at which the timer 1 was set.
And so on.
So, each timer is firing at 1000 * count.

Related

How to do a countdown with randomized time intervals, stopping at zero?

i would like to add another counter in this code:
function animateValue(id) {
var obj = document.getElementById(id);
var counter = getLocalStoregaValue();
var current = counter ? +counter : obj.innerHTML;
obj.innerHTML = counter;
setInterval(function() {
var counter = current--;
obj.innerHTML = counter;
localStorage.setItem('counter', counter);
}, 1000);
}
function getLocalStoregaValue() {
return localStorage.getItem('counter');
}
animateValue('value');
I would like it to scale once every second (as in this case) and once every 5 seconds. How can I? And then, how can I make it stop at 0? So without negative numbers. Thank you very much.
EDIT: I explained myself wrong.
I would like a single counter that drops in number from a minimum of 10 to a maximum of 20.
Example: the counter marks 50. After 15 seconds it marks 49. After 18 seconds it marks 48. After 11 seconds it marks 47. And so up to 0.
I hope I explained myself well this time :)
Ok, I was interrupted while posting my answer. Here now the explanation:
I left out the localStorage part of your question and concentrated on the generation of "independent countdowns" first:
function cntdwn(sel,stp,intv){
let el=document.querySelector(sel),
n=el.textContent-stp,
cd=setInterval(()=>{
el.textContent=n;
if((n-=stp)<0) clearInterval(cd);
}, intv);
}
cntdwn('#one',1,1000) ;
setTimeout(()=>cntdwn('#two',1,3000), 12000);
<p>first countdown:<br>step: 1, interval: 1s</p>
<p id="one">15</p>
<p>second countdown:<br>step: 1, action after: 15, 18, 21, 24 ... s (as mentioned in comment)</p>
<p id="two">50</p>
The cntdwn() function provides a scope in which individual countdowns can be set up for arbitrary DOM elements, each with their own counter (it starts with the value found in the DOM element), step-width and interval (in milliseconds).
Each countdown is generated with let cd=setInterval(...). The reference cd can then be used to stop the countdown (in clearInterval(cd)), once the value of n is found to be below zero.
Edit:
Assuming you made a typo in your sequence of intervals and you really meant: 15, 18, 21 seconds, then the edited second countdown should be the correct solution.
I used a setTimeout() function to delay the action by 12 seconds, then, after the first of the regular 3 second intervals (i. e. after a total of 15 seconds) the first change occurs. The countdown then continues in 3 second intervals until it reaches zero.
Yet another edit:
Ok, so you want: "A countdown with random time intervals (range 10 to 20s each) that will stop at zero"
This should do it:
function cntdwn(sel,int1,int2){
let el=document.querySelector(sel),
n=el.textContent-1,
cd=()=>setTimeout(()=>{
el.textContent=n;
if(n--) cd();
}, 1000*(int1+Math.random()*(int2-int1)));
cd();
}
cntdwn('#one',10,20);
<p>countdown:<br>step: 1, intervals: between 10 and 20 s</p>
<p id="one">5</p>
If you can use ES2017, you can use an asynchronous function to do it, like this:
async function animateValue(id) {
function timeout(t){
return new Promise(r => setTimeout(r, t))
}
var obj = document.getElementById(id);
var counter = getLocalStoregaValue();
for(let i = +counter || +obj.innerHTML || 0; i >= 0; i--){
obj.innerHTML = i;
localStorage.setItem('counter', i);
await timeout((Math.random() * 10 + 10) * 1000); //Pause for 10 to 20 seconds. For an integer second value, wrap `Math.random() * 10` into a `Math.floor` call
};
}
function getLocalStoregaValue() {
return localStorage.getItem('counter');
}
animateValue('value').catch(console.error);
<div id="value">50</div>
Try it (I commented out the localStorage part, as it isn't allowed in Stack Snippets):
async function animateValue(id) {
function timeout(t){
return new Promise(r => setTimeout(r, t))
}
var obj = document.getElementById(id);
var counter = getLocalStoregaValue();
for(let i = +counter || +obj.innerHTML || 0; i >= 0; i--){
obj.innerHTML = i;
//localStorage.setItem('counter', i);
await timeout((Math.random() * 10 + 10) * 1000); //Pause for 10 to 20 seconds. For an integer second value, wrap `Math.random() * 10` into a `Math.floor` call
};
}
function getLocalStoregaValue() {
//return localStorage.getItem('counter');
}
animateValue('value').catch(console.error);
<div id="value">50</div>

How to Change Interval Time Dynamically in For Loop According to Index/iteration Number?

Since I could not comment, I am forced to write this post. I got the below code which delays/waits exactly 1 seconds or 1000 milliseconds -
let n = 5;
for (let i=1; i<n; i++)
{
setTimeout( function timer()
{
console.log("hello world");
}, i*1000 );
}
But how can I delay it i*1000 seconds instead of fixed 1000 milliseconds so the waiting depends on iteration number ?
For example, if n= 5 , then I want the loop delay 1 second in 1st iteration. 2 seconds in second iteration, and so on.. the final delay will 5 seconds.
While this task could be solved with promises, reactive streams and other cool tools (hey, nobody has suggested using workers yet!), it can also be solved with a little arithmetics.
So you want timeouts in a sequence: 1s, the previous one + 2s, the previous one + 3s, and so on. This sequence is: 1, 3, 6, 10, 15... and its formula is a[n] = n * (n + 1) / 2. Knowing that...
let n = 6;
console.log(new Date().getSeconds());
for (let i = 1; i < n; i++) {
setTimeout(function timer() {
console.log(new Date().getSeconds());
}, 1000 * i * (i + 1) / 2);
}
Here is a function that will show immediately, then 1 second later, 2 seconds after than, 3 seconds after that etc. No special math, no promises needed
const n = 5;
let cnt=0;
function show() {
console.log("call "+cnt,"delay: ",cnt,"sec");
cnt++;
if (cnt > n) return; // we are done
setTimeout(show, cnt*1000 ); // cnt seconds later
}
show()
You can try using async/await (Promises), to serialize your code:
const waitSeconds = seconds => new Promise(resolve => setTimeout(resolve, seconds))
async function main () {
let oldDate = new Date()
let newDate
/*
* If you put 'await' inside the loop you can synchronize the async code, and simulate
* a sleep function
*/
for (let i=1; i<5; i++) {
await waitSeconds(i*1000)
newDate = new Date()
console.log(`Loop for i=${i}, elapsed=${moment(newDate).diff(oldDate, 'seconds')} seconds`)
oldDate = newDate
}
console.log('End')
}
main()
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
Took me some time to decipher your question xD, but is this what you want?
This will keep firing console.log with i*1000 delay each time.
so the first time it will be 1 second long (1*1000), next it will be 2 seconds and so on.
let i = 0;
loop = () => {
setTimeout(() => {
console.log(new Date()); // for clarity
i++;
if (i < 10) {
loop();
}
}, i * 1000)
};
loop();
Loop doesn't waits for timeout function to be completed.
So, when the loop runs it schedules your alert for each index.
You can use a function which will run according to your index but scheduled at same time. You can feel the difference of 3 seconds.
function test(i){
setTimeout( function timer(){
console.log("hello world" + i);
}, i*3000);
}
for (let i=1; i<4; i++) {
test(i);
}
Use recursive calls instead of for loop
let i=1;
function a(i) {
if (i > 5)
return
else
b("message", i)
}
function b(s, f) {
setTimeout(function timer() {
console.log(s + " " + f + " seconds");
}, f * 1000);
a(++i);
}
a(i);

Run a function for a specified amount of time and stop [duplicate]

I would like to repeat a text for 2 seconds in a while loop. How do I break the loop after 2 seconds?
This is what I have tried so far but it doesn't work:
var repeat = true;
setTimeout(function() { var repeat = false }, 2000)
while(repeat) {
console.log("Let's repeat for 2 seconds...");
}
Additionaly to the other answer you could just check the time instead:
const start = +new Date;
while(+new Date < start + 2000) {
console.log("Let's repeat for 2 seconds...");
}
JavaScript is single threaded. This means that as long as your loop runs, your timeout will never be fired.
Depending on what you want and whether or not you want to lock down your browser (by using an actual infinite loop), you can use the setInterval as a loop, and use the setTimeout to stop the interval after 2 seconds.
console.log("Starting loop");
var interval = setInterval(function () {
console.log("Let's repeat for 2 seconds...");
}, 0);
setTimeout(function() {
clearInterval(interval);
console.log("Finished loop");
}, 2000);
If you're concerned at all with performance of this loop, the proposed solution would be a problem.
Note that in most cases, you shouldn't be synchronously iterating a huge number of times in Javascript since that would completely block the browser or server during this time, but there are some cases where this might be needed. Just be aware that this is generally not a good thing to do.
What follows is my own experimentation with several options to reduce the overhead of checking a timeout, since I was using it to benchmark other code.
I added console.time logs to both solutions, and added a couple of optimizations that might be worth considering.
The accepted solution has the worst performance:
const label = 'interval';
console.time(label);
let i = 0;
const interval = setInterval(
() => {
i += 1;
},
0);
setTimeout(
() => {
clearInterval(interval);
console.timeEnd(label)
console.log(`${i.toExponential()} iterations`);
},
2000);
// interval: 2001.100ms
// 4.93e+2 iterations
The next answer has much better performance, but it's still performing unnecessary work, type conversions and addition on every loop run:
let i = 0;
let start = +new Date;
let label = '+new Date + 2000';
console.time(label);
while ((+new Date) < start + 2000) {
i += 1;
}
console.timeEnd(label)
console.log(`${i.toExponential()} iterations`);
// +new Date + 2000: 1999.800ms
// 1.0921121e+7 iterations
By using Date.now() instead of (+new Date) you get a performance increment of around 2.5x:
let label = 'Date.now()';
console.time(label);
let end = Date.now() + 2000;
let i = 0;
while (Date.now() < end) {
i += 1;
}
console.timeEnd(label)
console.log(`${i.toExponential()} iterations`);
// Date.now(): 1999.000ms
// 2.6477108e+7 iterations
If performance is much more important that the exact nanosecond when you stop, and the operations are super fast, you can reduce the number of checks for even more operations:
let label = 'fuzzy + 2000';
console.time(label);
let end = Date.now() + 2000;
let i = 0;
// Only check the timeout every 1000 operations thanks to lazy evaluation.
while (i % 1000 !== 0 || Date.now() < end) {
i += 1;
}
console.timeEnd(label)
console.log(`${i.toExponential()} iterations`);
// fuzzy + 2000: 1999.800ms
// 6.5632e+8 iterations
30x better! You will need to tweak the frequency of the checks based on both the average loop time and how exact you want your timeout to be.

Leave a loop after 2 seonds

I would like to repeat a text for 2 seconds in a while loop. How do I break the loop after 2 seconds?
This is what I have tried so far but it doesn't work:
var repeat = true;
setTimeout(function() { var repeat = false }, 2000)
while(repeat) {
console.log("Let's repeat for 2 seconds...");
}
Additionaly to the other answer you could just check the time instead:
const start = +new Date;
while(+new Date < start + 2000) {
console.log("Let's repeat for 2 seconds...");
}
JavaScript is single threaded. This means that as long as your loop runs, your timeout will never be fired.
Depending on what you want and whether or not you want to lock down your browser (by using an actual infinite loop), you can use the setInterval as a loop, and use the setTimeout to stop the interval after 2 seconds.
console.log("Starting loop");
var interval = setInterval(function () {
console.log("Let's repeat for 2 seconds...");
}, 0);
setTimeout(function() {
clearInterval(interval);
console.log("Finished loop");
}, 2000);
If you're concerned at all with performance of this loop, the proposed solution would be a problem.
Note that in most cases, you shouldn't be synchronously iterating a huge number of times in Javascript since that would completely block the browser or server during this time, but there are some cases where this might be needed. Just be aware that this is generally not a good thing to do.
What follows is my own experimentation with several options to reduce the overhead of checking a timeout, since I was using it to benchmark other code.
I added console.time logs to both solutions, and added a couple of optimizations that might be worth considering.
The accepted solution has the worst performance:
const label = 'interval';
console.time(label);
let i = 0;
const interval = setInterval(
() => {
i += 1;
},
0);
setTimeout(
() => {
clearInterval(interval);
console.timeEnd(label)
console.log(`${i.toExponential()} iterations`);
},
2000);
// interval: 2001.100ms
// 4.93e+2 iterations
The next answer has much better performance, but it's still performing unnecessary work, type conversions and addition on every loop run:
let i = 0;
let start = +new Date;
let label = '+new Date + 2000';
console.time(label);
while ((+new Date) < start + 2000) {
i += 1;
}
console.timeEnd(label)
console.log(`${i.toExponential()} iterations`);
// +new Date + 2000: 1999.800ms
// 1.0921121e+7 iterations
By using Date.now() instead of (+new Date) you get a performance increment of around 2.5x:
let label = 'Date.now()';
console.time(label);
let end = Date.now() + 2000;
let i = 0;
while (Date.now() < end) {
i += 1;
}
console.timeEnd(label)
console.log(`${i.toExponential()} iterations`);
// Date.now(): 1999.000ms
// 2.6477108e+7 iterations
If performance is much more important that the exact nanosecond when you stop, and the operations are super fast, you can reduce the number of checks for even more operations:
let label = 'fuzzy + 2000';
console.time(label);
let end = Date.now() + 2000;
let i = 0;
// Only check the timeout every 1000 operations thanks to lazy evaluation.
while (i % 1000 !== 0 || Date.now() < end) {
i += 1;
}
console.timeEnd(label)
console.log(`${i.toExponential()} iterations`);
// fuzzy + 2000: 1999.800ms
// 6.5632e+8 iterations
30x better! You will need to tweak the frequency of the checks based on both the average loop time and how exact you want your timeout to be.

JavaScript delay before looping

I am a complete noob at coding and have a quick question. Below is a snipet of code and what I need to do is, if the process repeats (i.e., ctr < end) then I need it to wait for 2 minutes before doing so. I don't want to lock down the computer, this will be running a background task. I have researched and while I might have found a solution in setTimeout(), I can't implement it properly with my limited knowledge. Thanks in advance for your help!
if (ctr < end) {
return function() {
otherFunction(ctr + 1);
};
Basic Example demonstrating setTimeout
var ctr = 0;
var end = 5;
mainLoop();
// some function
function mainLoop() {
// checks current ctr to end then increments ctr by 1
if (ctr++ < end) {
// log the value of ctr
console.log(ctr);
// This is set to happen every 2 seconds
// 2 minutes is 2 * 60 * 1000 = 120000 milliseconds
// calls the mainLoop function after 2000 milliseconds
setTimeout(mainLoop, 2000);
} else {
// prints it is over after the ctr is equal or greater than end
console.log('it is over');
}
}

Categories

Resources