I have this:
let time = store.time;
const run = setInterval(() => {
const delay = store.delay;
setTimeout(() => {
console.log("Execute event....");
}, delay * 1000);
time--;
if (time === 0) {
clearInterval(run);
}
}, 1000);
I just want to run a process indicating a time and delaying the time at the same time, for example:
time=10 ; delay=2, then the process will be executed when the time is equal to: 2, 4, 6, 8 and 10
time=10 ; delay=3, then the process will be executed when the time is equal to: 3, 6 and 9
time=10 ; delay=4, then the process will be executed when the time is equal to: 4 and 8
time=10 ; delay=5, then the process will be executed when the time is equal to: 5 and 10
It's not currently working that way, it's only running 10 times, why??
If you use a promise and async/await you can use a normal for loop to log the steps at the right intervals.
// Return a promise that resolves after a
// certain duration
function delay(step) {
return new Promise(res => {
setTimeout(() => res(), step * 1000);
});
}
// Accepts a limit, and a step count
async function loop(limit, step) {
// Log the first step and then...
console.log(step);
// ...loop over the rest of the numbers calling
// the delay function on each iteration
for (let i = step + step; i <= limit; i += step) {
await delay(step);
console.log(i);
}
}
const limit = 10;
const step = 2;
loop(limit, step);
You don't need to use both setInterval() and setTimeout(). Use setInterval(), with the interval being the delay converted to milliseconds. At each repetition, subtract the delay from the total number of times, and when this reaches the end, stop the timer.
let time = 10;
let delay = 3;
const run = setInterval(() => {
console.log("Execute event....");
time -= delay;
if (time < delay) {
console.log("Done");
clearInterval(run);
}
}, delay * 1000);
Related
inside JavaScript, how can I tell something to be done in a certain period of time? For example, for 5 seconds, the value of a variable will increase and it will stop after 5 seconds.
Of course, there are setinterval and settimeout, but they only do that work after that period of time is over. I want that work to be done during that time.
setInterval
You can use setInteveral, but it's mostly trash for everything I've ever attempted to use it for. In the code below, the counter will only increment to around 35 in 1 second; about 28 milliseconds per interval -
// initialize a counter
let counter = 0
// increment the counter on interval
let myprocess = setInterval(_ => counter++, 0)
// after 5 seconds, stop the process and log the counter value
setTimeout(_ => { clearInterval(myprocess); console.log(counter) }, 1000)
A more appropriate utility would be setImmediate. It is supported in Node but not in browsers yet. Polyfills are available if you plan to use this in the browser.
async generators
Above we see setInterval is quite slow. Another approach is to use an asynchronous generator. In the code below, the counter can reach over 300,000 in just 1 second; About 3.3 microseconds per interval.
async function *count(until) {
let counter = 0
while (Date.now() < until) {
yield counter++
}
}
async function main() {
for await (const n of count(Date.now() + 1000))
document.write(`${n}, `)
return "done"
}
main().then(console.log)
If the goal is to run the process as fast as possible for the fixed duration, we will skip expensive operations like document.write. In the example below the counter can reach over 1M in just 1 second; just under 1 microsecond per interval -
async function *count(until) {
let counter = 0
while (Date.now() < until) {
yield counter++
}
}
async function run(process, duration) {
let result
for await (const n of process(Date.now() + duration))
result = n
return result
}
run(count, 1000).then(console.log)
// 1045592
blocking while loop
If the loop itself is the main part of your program and you want simply want to run the counter as fast as possible for a fixed duration, use a while loop. Note this will block all other code from running for the entire duration of the loop -
function count(until) {
let n = 0
while (Date.now() < until)
n++
return n
}
setTimeout(console.log, 0, "I was blocked")
console.log(count(Date.now() + 1000))
// 36618673
// "I was blocked"
You will need to check inside some sort of loop if the time has passed, make sure the loop is async in nature or the JS main thread will have issues.
I have modified PI calculator from here Javascript: PI (π) Calculator to run for 5 seconds instead of infinite
, it's using requestAnimationFrame to keep things async->
function * generateDigitsOfPi() {
let q = 1n;
let r = 180n;
let t = 60n;
let i = 2n;
while (true) {
let digit = ((i * 27n - 12n) * q + r * 5n) / (t * 5n);
yield Number(digit);
let u = i * 3n;
u = (u + 1n) * 3n * (u + 2n);
r = u * 10n * (q * (i * 5n - 2n) + r - t * digit);
q *= 10n * i * (i++ * 2n - 1n);
t *= u;
}
}
// Demo
let iter = generateDigitsOfPi();
const tmStart = Date.now();//
let output = document.querySelector("div");
(function displayNextDigit() {
output.insertAdjacentHTML("beforeend", iter.next().value);
scrollTo(0, document.body.scrollHeight);
if (Date.now() - tmStart < 5000) requestAnimationFrame(displayNextDigit);
})();
div { word-wrap:break-word; font-family: monospace }
<div></div>
const setRunInSeconds = (callback, ms = 1000, delay = 1) => {
const intervalId = setInterval(callback, delay)
setInterval(() => {
clearInterval(intervalId)
}, ms)
}
let num = 0
setRunInSeconds(() => {
console.log('interval: ' + num)
num += 1
}, 5000, 1)
I need to run multiple timer one after another with pause. Input for timer comes from the array which contains set of times. I did something with reduce method. But could not break early the reduce method.
const times = [5, 4, 5];
function countDown(secs) {
console.log(secs);
if (secs === 1) {
clearTimeout(timer);
return timer;
}
secs--;
var timer = setTimeout(() => countDown(secs), 1000);
}
times.reduce((totalDelay, time) => {
setTimeout(() => {
countDown(delay);
}, 1000 * totalDelay);
const delay = parseInt(time);
totalDelay += delay;
return totalDelay;
}, 0);
I tried with a boolean to pause and play. The timer is paused by that boolean variable but when I resume two timer is running because of the reduce.
Is there any other way to do this with loop or something?
You code looks way too complicated. All you have to do is setting the timeout inside a function (without arguments) and use that function as a callback to setTimeout.
const times = [5, 4, 5];
function countDown() {
const secs = times.shift();
if (typeof secs === 'number') {
setTimeout(countDown, secs * 1000);
console.log(`Next timer in ${secs} seconds`);
} else {
console.log('Done');
}
}
countDown();
Here I'm removing an element from the start of the times array on each iteration. You could also leave the array unchanged and use an index to keep track of the progress.
I'm studying closures but I don't understand why the second is always 1 instead of 2, 3, 4, 5 respectively. What I understand is here that since each function (even iife) has its own scope. Each i variable is captured its own stack actually that's why it allows changes of values of i though had been var used.
In the same way, setTimeout should catch each different i variables having 1,2,3,4,5 (seconds) respectively. But, it doesn't seem such as seen in the image. Could you help?
Maybe it is helpful that i is not a free variable for
setTimeout. (no idea it is of any relation)
Code
for (let i = 1; i <= 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i)
}, i * 1000)
})(i)
}
I think the use of let and an IIFE is throwing you off. Using an IIFE creates a new scope for the function parameters. A less known fact is that using let in a loop creates a new scope on every iteration.
e.g.
Using var and an IIFE.
// 'i' is declared here once
for (var i = 1; i <= 5; i++) {
// 'i' is declared again and takes a new scope every time the function is executed
(function (i) {
setTimeout(function () {
console.log(i) // references the nearest-scoped 'i'
}, i * 1000)
})(i)
}
// => 1, 2, 3, 4, 5
Using var and no IIFE
// 'i' is declared here once
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i) // references nearest-scoped 'i'
}, i * 1000)
}
// => 6, 6, 6, 6, 6
Using let and no IIFE
// 'i' is given a new scope in every iteration
for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i)
}, i * 1000)
}
// => 1, 2, 3, 4, 5
Using let and an IIFE. This is exactly your example.
// 'i' is given a new scope in every iteration
for (let i = 1; i <= 5; i++) {
// 'i' is declared again and takes a new scope every time the function is executed
(function (i) {
setTimeout(function () {
console.log(i) // references the nearest-scoped 'i'
}, i * 1000)
})(i)
}
// => 1, 2, 3, 4, 5
I don't understand too how the engine works, albeit i gives results as expected. I wouldn't expect persistently 1 second elapsing.
for (let i = 1; i <= 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i)
}, (console.log(i),i * 1000))
})(i)
}
In a comment you've said:
I expect that 1 second later 1, 2 secs later 2, 3 secs later 3, 4 secs later 4, 5 secs later 5. But 1,2,3,4,5 are printed 1 second elapsed each time as in the image
That would be what would happen if you started the timer for 2 after the callback for timer 1 was called, but that's not what the code does. The code sets up the five timers all right away — a timer to show 1 after one second, a timer to show 2 after two seconds, a timer to show 3 after three seconds, etc. Since they all start at the same time, you see 1 after one second, 2 a second after that (two seconds after it was scheduled), 3 a second after that (three seconds after it was scheduled), etc.
To see that, let's show when we start each timer, and also how long it's been since the previous one fired. First, though, you don't need your IIFE, because you're using let in a for statement, so each loop iteration gets its own i variable. So here's your original code without the IIFE but adding the message when each timer starts and showing how long it was between when the timer started and when it fired; this will still just do them roughly one second apart instead of doing what you said you want:
// Still doesn't do what you want, but shows that the IIFE isn't needed
// and when each timer is set up
let previousFired = Date.now();
for (let i = 1; i <= 5; i++) {
console.log(`setting timer to show ${i} in ${i * 1000}ms`);
const started = Date.now();
setTimeout(function () {
console.log(`${i} - fired after ${Date.now() - started}ms (${Date.now() - previousFired}ms after last)`);
previousFired = Date.now();
}, i * 1000);
}
If you want to wait one second to see 1, then wait a further two seconds to see 2, etc., either don't start the next timer until the previous one fires, or schedule them later. The latter is simpler, so let's do that:
let total = 0;
let previousFired = Date.now();
for (let i = 1; i <= 5; i++) {
total += i;
console.log(`setting timer to show ${i} in ${total * 1000}ms`);
const started = Date.now();
setTimeout(function () {
console.log(`${i} - fired after ${Date.now() - started}ms (${Date.now() - previousFired}ms after last)`);
previousFired = Date.now();
}, total * 1000);
}
You can handle async loops awaiting a timedout Promise, then you can call a external function:
function f(i) {
console.log(i)
}
const runLoop = async () => {
for(let i = 1; i <= 5; i++){
await new Promise(resolve => setTimeout(resolve, i * 1000))
f(i * 1000);
}
}
runLoop()
This is my script:
var find = setInterval(function() {
if (document.getElementsByClassName('RDlrG Inn9w iWO5td')[0]) {
if (document.getElementsByClassName('w1OTme')[0]) {
window.open(document.getElementsByClassName('w1OTme')[0].href);
//here I call the setTimeout function for my SetInterval
}
}
}, 2000);
This is a Tampermonkey script I am developing for Google Calendar.
I want to set a timeout function on my find function aka setInterval function so it doesn't spam the window.open function.
In short:
Is there a way I could set a Timeout function on setInterval function which is called from my setInterval function?
If yes, how so?
You can't pause the interval of a setInterval, but you can stop it and start it again after some time.
let find = null;
function intervalFunc() {
if (condition) {
// Do some operations which should not be repeated for the next 30 seconds
// Clear current interval
clearInterval(find);
// Schedule to start the setInterval after 30 seconds.
setTimeout(function() {
find = setInterval(intervalFunc, 2000);
}, 30000 - 2000);
// ^
// Subtracting the interval dalay to cancel out the delay for the first invocation.
// (Because the first invocation will also wait for 2 seconds, so the pause would be 32 seconds instead of 30)
}
}
// Start the initial setInterval
find = setInterval(intervalFunc, 2000);
Here is a working example:
let count = 0;
const intervalDelay = 200;
const pauseDelay = 3000;
let find = null;
function intervalFunc() {
count++;
console.log('check', count);
if (count >= 5) {
count = 0;
console.log('Pausing for ' + (pauseDelay / 1000) + ' seconds');
clearInterval(find);
setTimeout(function() {
find = setInterval(intervalFunc, intervalDelay);
}, pauseDelay - intervalDelay);
}
}
find = setInterval(intervalFunc, intervalDelay);
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);