Confusion of setInterval with setTimeout in JavaScript - javascript

I am trying to use setInterval with setTimeout in JavaScript.
var temArr = []
let timer = setInterval(() => {
var target = Math.random()
temArr.push(target)
console.log(6, target, temArr[temArr.length - 1], target === temArr[temArr.length - 1]) // one
setTimeout(() => {
console.log(9, target, temArr[temArr.length - 1], target === temArr[temArr.length - 1]) // two
}, 500);
}, 100)
setTimeout(() => {
clearInterval(timer)
}, 10000);
I expected that the last value of one and two would be true.
I expected that every time when the logger print, both one and two have the same value of the third parameter, that means target equals temArr[temArr.length - 1]
But the result is that one is true, and two is false
But the intermediate result of two is false, and the last value is true, while one is alway true
When I switch the timeout value, and the result is true that means set the interval with value 500 and the timeout with value 100, at this time both target === temArr[temArr.length - 1] is alway true every time the logger print
could you please tell me why?

They both returned true for me, just different order:
Ok setInterval VS setTimeout:
setInterval - will run the function inside EVERY x ms. which means:
setInterval(() => {
console.log('I will run every 100ms')
}, 100)
Will run EVERY 100ms.
setTimeout - will run the function inside AFTER x ms. which means:
setTimeout(() => {
console.log('I will run after 10 seconds')
}, 10000);
will run AFTER 10 seconds.
So doing this:
let timer = setInterval(() => {
console.log('I will run every 500ms';
setTimeout(() => {
cosole.log('I will run AFTER 500ms EVERY 500ms');
}, 500);
}, 100)
The log inside setTimeout will run 500ms AFTER the first log has been triggered and will trigger every 100ms.
EDIT - Answer to user's edited question:
For clearer logs, I modified your code:
var temArr = []
var intervalOrder = 0;
var timeoutOrder = 0;
var timer = setInterval(() => {
var target = Math.random()
temArr.push(target)
intervalOrder++
console.log('ONE - SET TIMEOUT: ', {order: intervalOrder, target, tem: temArr[temArr.length - 1], result: target === temArr[temArr.length - 1]})
setTimeout(() => {
timeoutOrder++
console.log('TWO - SET TIMEOUT: ', {order: timeoutOrder, target, tem: temArr[temArr.length - 1], result: target === temArr[temArr.length - 1]}) // two
}, 500);
}, 100)
setTimeout(() => {
clearInterval(timer)
}, 1000);
Notice how TWO - SET TIMEOUT doesn't fetch the same result as ONE - SET TIMEOUT? It's because it doesn't fetch the value of that variable when it was called but it fetches ON TRIGGER which means that the value already changed because setInterval has shorter time.
What you can do for this is to call the setTiemout on a different function so it will reference its value on the parameter instead of the newly generated one.
var temArr = []
var intervalOrder = 0;
var timeoutOrder = 0;
var logSecond = (target) => {
setTimeout(() => {
timeoutOrder++
console.log('TWO - SET TIMEOUT: ', {order: timeoutOrder, target, tem: temArr[temArr.length - 1], result: target === temArr[temArr.length - 1]}) // two
}, 500);
}
var timer = setInterval(() => {
var target = Math.random()
temArr.push(target)
intervalOrder++
console.log('ONE - SET TIMEOUT: ', {order: intervalOrder, target, tem: temArr[temArr.length - 1], result: target === temArr[temArr.length - 1]})
logSecond(target)
}, 100)
setTimeout(() => {
clearInterval(timer)
}, 1000);
^^ on the snippet above, the target and result are now the same for both logs.

Related

JavaScript execute event each every time

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);

How to run multiple timers one after another with pause in javacript?

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.

How to return the first 2 objects of an array of 4 objects

I want to return the first 2 objects of an array of 4 objects then add the other two objects with 5 seconds in between.
note: I am reversing the copied array revEvents with reverse() as items are in descending order by date/time, the most recent item goes on top.
My current issue is the first two objects are displayed ok, then after 5 seconds, it loads only the third object and it stops.
useEffect(() => {
let ms = 3000
let i = 0
let displayedEvents = [...props].splice(i, 2)
setEventsProps(displayedEvents.reverse())
const interval = setInterval(() => {
if (++i <= props.length) {
displayedEvents = props.splice(0, i + 2)
setEventsProps(displayedEvents.reverse())
}
}, ms)
return () => { clearInterval(interval) }
}, [])
//JSX as below
displayedEvents.map(event () => ...etc
I'd like to share an improved solution.
useEffect(() => {
const ms = 5000
let i = 2
const displayedEvents: IEventsProps = props.slice(0, 2)
setEventsProps(displayedEvents.reverse())
let interval: NodeJS.Timeout = setInterval((): void => {
if (i < props.length) {
displayedEvents.push(props[i])
setEventsProps(displayedEvents.reverse())
i += 1
} else {
clearInterval(interval)
interval = null
}
}, ms)
return () => { if (interval) { clearInterval(interval) } }
}, [])
This avoids making unnecessary new arrays by mutating the same array, and also clears the interval when the work is done, so it doesn't run forever.
Fixed this issue, instead of using the splice() function I needed to use the slice() function instead.
The code remains the same, added typescript for anyone who find this.
useEffect(() => {
let ms: number = 5000
let i: number = 0
let displayedEvents: IEventsProps = [...props].slice(i, 2)
setEventsProps(displayedEvents.reverse())
const interval: NodeJS.Timeout = setInterval((): void => {
if (++i <= props.length) {
displayedEvents = props.slice(0, i + 2)
setEventsProps(displayedEvents.reverse())
}
}, ms)
return () => { clearInterval(interval) }
}, [])

Why there is an extra execution of setInterval's function if we change order of setTimeout and setInterval?

Why code 1 runs 9 times and code 2 runs 10 times. Both are async operations and both should run in a separate thread (in the background). But why 1 extra iteration for code 2. Does setTimeout and setInterval has priority or it just First come first serve execution?
CODE 1 codepen
let isValid = true,
counter = 0;
setTimeout(() => {
isValid = false;
clearInterval(id);
}, 2000);
const id = setInterval(() => {
if (isValid) {
console.log(counter++);
}
}, 200);
CODE 2 codepen
let isValid = true,
counter = 0;
const id = setInterval(() => {
if (isValid) {
console.log(counter++);
}
}, 200);
setTimeout(() => {
isValid = false;
clearInterval(id);
}, 2000);
let isValid = true,
counter = 0;
setTimeout(() => {
isValid = false;
clearInterval(id);
}, 2001);
const id = setInterval(() => {
if (isValid) {
console.log(counter++);
}
}, 200);
This is happening just because of delay in the execution in both cases as if we just increment the timeout just be 1 millisecond it prints 9 thus the delay is probably less than 1 millisecond to.
Well the delay may be different for different browsers.
Chrome : less than 1ms
Edge : less than 1ms
Mozilla :
Internet Explorer: Well... The website doesn't open there

setTimeout() being called immediately in throttle function

so I'm trying to write a throttle function that when called, calls a callback but only up to a certain limit number of times in a given interval. If the limit is reached, the callback gets pushed into a queue where the callback gets called after the initial interval.
const throttle = (cb, interval, maxCalls) => {
let calls = 0;
const records = [];
let totalCalls = 0;
return (...rest) => {
if(calls < maxCalls) {
calls++;
records.push(Date.now());
totalCalls++;
cb.apply(null, rest);
setTimeout(() => {
calls--;
}, interval);
} else {
//cb within setTimeout being invoked immediately here
setTimeout(() => {
calls++;
records.push(Date.now());
totalCalls++;
cb.apply(null, rest);
//console.log(allotedTime: interval - (Date.now() - records[(totalCalls-1)-maxCalls]));
}, interval - (Date.now() - records[(totalCalls-1)-maxCalls]));
}
}
}
const meow = (start, ...args) => {
console.log(Date.now() - start, ...args);
}
const burp = throttle(meow.bind(this, Date.now()), 10000, 2);
setTimeout(() => burp('burp'), 0); // expect 2-7 'burp'
setTimeout(() => burp('burp'), 5000); // expect 5000 'burp'
setTimeout(() => burp('burp'), 6000); // expect 10000 'burp'
setTimeout(() => burp('burp'), 7000); // expect 15000 'burp'
The main problem is that for some reason, within the else block, the function isn't waiting for the setTimeout and is being called immediately. The syntax seems fine so I'm having a hard time figuring out why it's being called. This is the output after being called:
setTimeout(() => burp('burp'), 0); //6 'burp'
setTimeout(() => burp('burp'), 5000); //5001 'burp'
setTimeout(() => burp('burp'), 6000) //6001 'burp'
//allotedTime: 4005
setTimeout(() => burp('burp'), 7000); //10008 'burp'
//allotedTime: 4993
You'll notice that if you add the allotedTime with the result from the line above, you'll get the desired log. Thanks for taking a look.
Link to repl
Not sure if I understood the problem entirely, but based on your expectations,
the else interval should be:
interval - (Date.now() - records[totalCalls-maxCalls]))
because totalCalls is increment once the callback is called. So, either add totalCalls++; as the first statement of the else block (before setInterval()) or don't expect the value to be incremented (suggestion number 1).

Categories

Resources