Javascript call function at each "X" minutes by clock - javascript

Ok, so I have:
function show_popup() {
alert('Ha');
}
Now, what I want is to call this function at each X minutes BUT giving as reference the clock (the real time).
If X is 5, then the next function works properly:
setInterval(function(){
var date = new Date();
var minutes = date.getMinutes().toString();
var minutes = minutes.slice(-1); // Get last number
if(minutes == 0 || minutes == 5)
{
show_popup(); // This will show the popup at 00:00, 00:05, 00:10 and so on
}
}, 1000);
How can I make this function to work if I change 5 minutes to 4, or to 3, or to 20 ?
I must mention that I can't change the timer from setinterval, cause this it will mean that the popup will trigger only if you are on page AFTER passing X minutes. I don't want that. I want to show the popup at specific minutes giving the reference the clock.

You need to find the multiples of X
To do that, you can use modulo operation, so:
if(minutes % X === 0) {
show_popup();
}
The modulo operation will return the rest of division between a and b, if thats 0, thats means b is multiple of a.
For example, if you want to show every 3 minutes:
1 % 3 = 1
2 % 3 = 2
3 % 3 = 0 //show
4 % 3 = 1
5 % 3 = 2
6 % 3 = 0 //show
And so on...

two ways, just run the code to see results(in chrome browser)
1.use timer and you can change period when next tick comes, timer is not that precise
class MyInterval {
constructor(defaultInterval, callback) {
this.interval = defaultInterval
this.callback = callback
this._timeout = null
this.tick()
}
tick() {
const {
interval,
callback
} = this
this._timeout = setTimeout(() => {
callback()
this.tick()
}, interval)
}
stop() {
clearTimeout(this._timeout)
}
changeInterval(interval) {
this.interval = interval
}
}
const myInterval = new MyInterval(1000, () => console.log(new Date()))
setTimeout(() => {
myInterval.changeInterval(2000)
}, 3500)
setTimeout(() => {
myInterval.stop(2000)
}, 13500)
2.use a minimal interval, more quick to react, has a minimal limit, may cost more
class MyInterval {
constructor(minimal, defaultInterval, callback) {
this.minimal = minimal
this.interval = defaultInterval
this.callback = callback
this._current = 0
this._timeout = setInterval(() => {
this._current++
if (this._current >= this.interval) {
this._current = 0
callback()
}
}, minimal)
}
stop() {
clearInterval(this._timeout)
}
changeInterval(interval) {
this.interval = interval
}
}
const myInterval = new MyInterval(1000, 1, () => console.log(new Date()))
setTimeout(() => {
myInterval.changeInterval(2)
}, 3500)
setTimeout(() => {
myInterval.stop()
}, 13500)

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

How to make setInterval method have only one instance running

I have a button that, when clicked, starts a timer and decreases the integer by 1 per second.
However, if the button is clicked multiple times, the integer decreases by a lot more than 1 per second, due to setInterval being called multiple times.
How do I make it so that, even though the button is clicked multiple times, the timer only goes down by 1 each second (I do not want to disable the button).
var time = document.getElementById("time");
document.getElementById("Btn").addEventListener("click", () => {
var decreaseTime = setInterval(() => {
if (time.textContent != 0) {
time.innerHTML = time.textContent - 1;
}
}, 1000);
if (time.textContent == 0) {
clearInterval(decreaseTime);
}
});
You should define your decreaseTime variable in a higher scope and then check if it exists prior to defining your interval. For instance:
var time = document.getElementById("time");
decreaseTime = undefined;
document.getElementById("Btn").addEventListener("click", () => {
if(!decreaseTime){
decreaseTime = setInterval(() => {
if (time.textContent != 0) {
time.innerHTML = time.textContent - 1;
}
}, 1000);
if (time.textContent == 0) {
clearInterval(decreaseTime);
}
}
});

Javascript - telling setInterval to only fire x amount of times?

Is it possible to limit the amount of times that setInterval will fire in javascript?
You can call clearInterval() after x calls:
var x = 0;
var intervalID = setInterval(function () {
// Your logic here
if (++x === 5) {
window.clearInterval(intervalID);
}
}, 1000);
To avoid global variables, an improvement of the above would be:
function setIntervalX(callback, delay, repetitions) {
var x = 0;
var intervalID = window.setInterval(function () {
callback();
if (++x === repetitions) {
window.clearInterval(intervalID);
}
}, delay);
}
Then you can call the new setInvervalX() function as follows:
// This will be repeated 5 times with 1 second intervals:
setIntervalX(function () {
// Your logic here
}, 1000, 5);
I personally prefer to use setTimeout() spaced out to achieve the same effect
// Set a function to run every "interval" seconds a total of "x" times
var x = 10;
var interval = 1000;
for (var i = 0; i < x; i++) {
setTimeout(function () {
// Do Something
}, i * interval)
}
There's no clean up required with clearInterval()
You can enclose it to avoid variables leaking and it looks pretty clean :)
// Definition
function setIntervalLimited(callback, interval, x) {
for (var i = 0; i < x; i++) {
setTimeout(callback, i * interval);
}
}
// Usage
setIntervalLimited(function() {
console.log('hit'); // => hit...hit...etc (every second, stops after 10)
}, 1000, 10)
You can set a timeout that calls clearInterval.
This should work:
function setTimedInterval(callback, delay, timeout){
var id=window.setInterval(callback, delay);
window.setTimeout(function(){
window.clearInterval(id);
}, timeout);
}
You can use setTimeout and a for loop.
var numberOfTimes = 20;
delay = 1000;
for (let i = 0; i < numberOfTimes; i++) {
setTimeout( doSomething, delay * i);
}
This will clear the interval after 10 calls
<html>
<body>
<input type="text" id="clock" />
<script language=javascript>
var numOfCalls = 0;
var int=self.setInterval("clock()",1000);
function clock()
{
var d=new Date();
var t=d.toLocaleTimeString();
document.getElementById("clock").value=t;
numOfCalls++;
if(numOfCalls == 10)
window.clearInterval(int);
}
</script>
</form>
</body>
</html>
I made a small package that does this for NodeJS.
https://www.npmjs.com/package/count-interval
It's a drop-in replacement for setInterval (including parameter passing), but it takes an additional count parameter. This example prints a message once every second, but only 3 times.
const countInterval = require('./countInterval');
const timer = countInterval(() => {
console.log('fired!', new Date());
}, 1000, 3);
And for those of you preferring setTimeout and loving recursion here is my suggestion ;)
const setIntervalX = (fn, delay, times) => {
if(!times) return
setTimeout(() => {
fn()
setIntervalX(fn, delay, times-1)
}, delay)
}
Then as suggested you can call the new setInvervalX() function as follows:
// This will be repeated every for 5 times with 1 second intervals:
setIntervalX(function () {
// Your logic here
}, 1000, 5);
You can do this actually very simply with setTimeout() and an incremental counter.
var i = 0; // counter for the timer
function doSomething() {
console.log("1 second"); // your actual code here, alternatively call an other function here
if (++i < 10)
{ // only reset the timer when maximum of 10 times it is fired
console.log("reset the timer");
setTimeout(doSomething, 1000); // reset the timer
}
}
setTimeout(doSomething, 1000); // init the first
This answer is based on SO: Repeating setTimeout and a nice, neat and tidy small combination with this.
You can use Six
SetIntervalX: Limit the number of times that setInterval will fire
import { setIntervalX } from "https://deno.land/x/six/mod.ts";
import { randomNumber } from "https://deno.land/x/random_number/mod.ts";
const API_URL = "https://leap.deno.dev";
async function checkAPIStatus() {
const startTime = performance.now();
const randomYear = randomNumber({ min: 2000, max: 10_000 });
const response = await fetch(`${API_URL}/${randomYear}`);
const data = await response.json();
console.log(`Is ${randomYear} a leap year? ${data.leapYear}.`);
const entTime = performance.now();
console.log(`Request took ${(entTime - startTime) / 1000} seconds.`);
}
setIntervalX(checkAPIStatus, 2000, 15);
Web Page: https://ulti.js.org/six
Repository: https://github.com/UltiRequiem/six
It includes documentation, 100% code coverage, and examples!
Works on Deno, Node.js and the browser!

Categories

Resources