Ignore the function call for a specific time - javascript

I am looking for an optimal solution for the described problem.
Scenario: There is a function getData() which is being called every second.
If it is called now I want to ignore any call to this function for let say 5 sec.
how best we can achieve this in javascript.

Save last call time and check if passed more than 5 seconds:
var lastCall = 0;
function getData() {
if (lastCall >= moment().subtract(5, 'mins').unix()) {
return;
}
lastCall = moment().unix();
/* rest of code */
}

Add a flag and toggle it after 5 seconds and on each not ignored call:
var shouldIgnore = false;
function getData() {
if (shouldIgnore) {
return;
}
shouldIgnore = true;
setTimeout(() => {
shouldIgnore = false;
}, 5000);
/* rest of code */
}

There are may may using setTimeout you can do. I have give sample example with some util mathod to make it simpler.
Throttle Function:
const throttle = (fn, ms = 0) => {
let lastRunTime;
return function(...args) {
const currTime = +new Date();
if (!lastRunTime || currTime - lastRunTime > ms) {
lastRunTime = +new Date();
fn.apply(this, args);
}
};
};
How to use it:
(async function throttleEx() {
const logTill1Sec = throttle(log, 1 * 1000);
logTill1Sec("deepakt_1");
await new Promise(r => setTimeout(r, 500)); //2 sec virtual delay
logTill1Sec("deepak_t2");
})();
Output:
Mr. deepakt_1
Here you notice, even I call logAfter5Sec multiple times. It execute last one. You can write same way call once.
const throttle = (fn, ms = 0) => {
let lastRunTime;
return function(...args) {
const currTime = +new Date();
if (!lastRunTime || currTime - lastRunTime > ms) {
lastRunTime = +new Date();
fn.apply(this, args);
}
};
};
(async function throttleEx() {
const logTill1Sec = throttle(log, 1 * 1000);
logTill1Sec("deepakt_1");
await new Promise(r => setTimeout(r, 500)); //2 sec virtual delay
logTill1Sec("deepak_t2");
})();
const debounce = (fn, ms = 0) => {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), ms);
};
};
const dLog = debounce(log, 200); //ms time
dLog("deepak11");
dLog("deepak22");
dLog("deepak33");
function log(name) {
console.log(`Mr. ${name}`);
}
(async function() {
const logAfter5Sec = debounce(log, 1 * 1000);
logAfter5Sec("deepak");
await new Promise(r => setTimeout(r, 500)); //2 sec virtual delay
logAfter5Sec("deepak2");
})();

Related

Make JS function output while function running

Javascript code
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 1000000;
var money = 0
var i = 0
let dollars = document.getElementById("total")
let dollarclick = document.getElementById("click1")
let autoclick = document.getElementById("buyauto")
dollarclick.addEventListener("click", clicked)
autoclick.addEventListener("click", auto)
function clicked() {
money = money + 1
dollars.innerHTML = "$"+ money.toString()
}
function auto() {
autoclick.disabled = true
console.log("function skull")
while (i<10) {
money = money + 1
dollars.innerHTML = "$"+ money.toString()
sleep(1000)
console.log(money)
i++
}
}
How to make the function output while it is running, and not let it output after the function is already finished? Keep in mind I am making a cookie clicker type game, with the auto being able to be purchased only once. Can you help me resolve this problem? The game should be able to give the points every second, and not after the function has finished. Please help me, thank you!
You can do this in an asynchronous manner.
I have used a function (setTimeoutAsync from #3 here) that resolves a promise after a timeout, and an asynchronous recursive function (nClicks(n)) that uses await for the timeout before performing the action and awaits recursion n times before returning.
Then, the asynchronous event handler just needs to disable the button and wait for nClicks(10) to complete before doing other stuff like re-enabling the button. If you don't need to do other stuff afterwards you can omit the async declaration on the handler and just call nClicks(10) without await.
const setTimeoutAsync = function(timeout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, timeout)
})
}
let money = 0
const dollars = document.getElementById('total')
const tick = () => {
dollars.innerHTML = `$${++money}`
console.log('tick', dollars.innerHTML)
}
const nClicks = async(n) => {
if (n) {
// wait one second
await setTimeoutAsync(1000)
// then incriment
tick()
// wait for recursion to complete
await nClicks(--n)
}
}
const dollarclick = document.getElementById('click1')
const autoclick = document.getElementById('buyauto')
dollarclick.addEventListener('click', tick)
autoclick.addEventListener('click', async() => {
autoclick.disabled = true
// wait for 10 cycles
await nClicks(10)
autoclick.disabled = false
})
const autoclickOnce = document.getElementById('buyautoOnce')
autoclickOnce.addEventListener('click', () => {
autoclickOnce.disabled = true
nClicks(10)
})
<h1 id="total">$0</h1>
<button id="click1">+1</button>
<button id="buyauto">Auto</button>
<button id="buyautoOnce">Auto Once</button>

Javascript : Call function and wait seconds then

I feel stupid because I do not find what I want to do...
It is in PURE Javascript.
I wan't to call a function, and stop it (or kill it, or whatever) next some seconds.
Here is my actual code :
function scrollThumbnails() {
const thumbnails = document.querySelectorAll('.thumbnail');
for (thumbnail of thumbnails) {
thumbnail.classList.add('active');
await verticalSlider(thumbnail);
resetSliderPosition(thumbnail);
thumbnail.classList.remove('active');
}
scrollThumbnails();
}
async function verticalSlider(element) {
const slider = element.querySelector('.vertical-carrousel');
const max = slider.offsetHeight - slider.parentElement.offsetHeight;
var toTop = 0;
while (toTop > -max) {
await new Promise((resolve) => setTimeout(resolve, 50));
toTop--;
slider.style.top = toTop + 'px';
}
await new Promise((resolve) => setTimeout(resolve, 1000));
}
function resetSliderPosition(element) {
const slider = element.querySelector('.vertical-carrousel');
slider.style.removeProperty('top');
}
So here, i want to call 'verticalSlider' in my loop, but if it is over than 45 seconds, I want to stop it and go on the next thumbnail.
Is it possible ? Do I miss something ?
Thanks by advanced :)
This is simple example without async and await with manual timeout counter implementation.
// Function, which accepts time out value in seconds (counting from 0)
const myFunct = (timeOutSec) => {
// Get current time
const ct = Date.now();
// Set counter for example function logic
let secCounter = ct - 1000;
// Start eternal loop
while(true) {
// Get in loop time
const ilt = Date.now();
// Compare current time and in loop time
// If current time + timeout sec < in loop time, break it
if(ct + timeOutSec * 1000 < ilt) break;
// Here do main function logic in loop
// for example, console log each second
if(ilt - secCounter > 1000) {
console.log(`time ${ilt}`);
secCounter += 1000;
}
}
}
// Test running function for 4 sec
myFunct(3);
console.log(`Completed at ${Date.now()}`);
Or similar with promises
// Async timeout function
const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Promise function which accepts timout value in seconds (counting from 0)
const myPFunct = (timeOutSec) => {
return new Promise(async (resolve, reject) => {
// Current time
const ct = Date.now();
// start eternal loop
while(true) {
// In loop time
const ilt = Date.now();
// Check in-loop time, current time and timeout
if(ct + timeOutSec * 1000 < ilt) {
resolve(`completed at ${ilt}`);
break;
}
// Do something
console.log(`time: ${ilt}`);
// Wait 1 sec
await timeout(1000);
}
});
}
// Test async function
const test = async () => {
// Run myPFunct for 4 sec
const result = await myPFunct(3);
console.log(`Test function result: ${result}`);
}
// Run test function
test();
Thanks to #tarkh solution.
If someone try to do an horizontal slider like me, or something with loop, I have to modify the code (because with the source solution, the resolve goes up to all the async methods).
Here is the final solution (I have to name the loop to break it and resolve the current promise - there are maybe simplest solution ?) :
window.onload = function () {
scrollThumbnails();
}
const scrollThumbnails = async () => {
const thumbnails = document.querySelectorAll('.thumbnail');
for (thumbnail of thumbnails) {
thumbnail.classList.add('active');
await verticalSliderWithTimeout(thumbnail, 45000);
thumbnail.classList.remove('active');
}
scrollThumbnails();
}
const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const verticalSliderWithTimeout = (element, timeoutDelay) => {
return new Promise(async (resolve, reject) => {
const currentTime = Date.now();
const slider = element.querySelector('.vertical-carrousel');
const max = slider.offsetHeight - slider.parentElement.offsetHeight;
var toTop = 0;
slider.style.top = toTop + 'px';
sliderLoop: while (toTop > -max) {
const inLoopTime = Date.now();
if(currentTime + timeoutDelay < inLoopTime) {
break sliderLoop;
}
await timeout(50);
toTop--;
slider.style.top = toTop + 'px';
}
resolve();
});
}
HTML code if needed (Handlebars) :
<div class="thumbnail">
<div class="photos-slider">
<div id="vertical-carrousel" class="vertical-carrousel">
{{#each this.photos}}
<img src="/image/{{../this.agency.product_type}}/{{../this.agency.id}}/{{../this.reference}}/{{urlencode this}}"
onerror="{{this}}" />
{{/each}}
</div>
</div>
</div>

How do I switch the running codes using the IF?

Bear with me, I just started learning JS yesterday trying to make a HTML clock by putting multiple tutorials' results together.
I made two looping animations using 2 arrays, with the intent of switching between arrays depending on if it's earlier or later than 8pm. I would like for my code to constantly check if it's 8pm every X amount of seconds, and only re-run the script if the IF result or output is different than what it currently is.
const now = new Date();
if (now.getHours() > 20) { // Time checker!!!!!
var hat = ['A','B','C','D'],
a = -1;
(function f(){
a = (a + 1) % hat.length;
const hatformatted = `${(hat[ a ])}`;
document.querySelector(".main").textContent = hatformatted;
setTimeout(f, 1000);
})();
} else {
var hat = ['1','2','3','4'],
a = -1;
(function f(){
a = (a + 1) % hat.length;
const hatformatted = `${(hat[ a ])}`;
document.querySelector(".main").textContent = hatformatted;
setTimeout(f, 1000);
})();
}
I have tried setInterval however it re-runs the script even if the IF result has not changed, and messes up the animation.
I.E - if it's 6pm, it tries to play the animation from the start every 1 second and the frames get messed up.
Any advice or help would be great, thanks!
I tried to save your code.
const checkTimeDelay = 1000;
let isAbove8PM = null;
let curTimeHandler = null;
let intervalId = null;
// checking if is above 8 pm
const checkTime = () => {
const now = new Date();
if (now.getHours() > 20) {
return true;
}
return false;
};
const above8PMHandler = () => {
var hat = ['A','B','C','D'],
a = -1;
return () => {
a = (a + 1) % hat.length;
const hatformatted = `${(hat[ a ])}`;
document.querySelector(".main").textContent = hatformatted;
// setTimeout(f, 1000);
};
};
const before8PMHandler = () => {
var hat = ['1','2','3','4'],
a = -1;
return () => {
a = (a + 1) % hat.length;
const hatformatted = `${(hat[ a ])}`;
document.querySelector(".main").textContent = hatformatted;
// setTimeout(f, 1000);
};
};
// restart clock interval on based on new handler (above8PMHandler|before8PMHandler)
const rebaseClockInterval = () => {
clearInterval(intervalId);
intervalId = setInterval(curTimeHandler, 1000);
};
// main func, checks if we should change clocks handler (above8PMHandler|before8PMHandler)
const clockTick = () => {
const curTimeChecked = checkTime();
if (curTimeChecked === isAbove8PM) { return; }
isAbove8PM = curTimeChecked;
if (isAbove8PM) {
curTimeHandler = above8PMHandler();
} else {
curTimeHandler = before8PMHandler();
}
curTimeHandler();
rebaseClockInterval();
};
// start main func
setInterval(clockTick, checkTimeDelay);
I also provide some ideas here.
function setClockInterval() {
const hat1 = ['A', 'B', 'C', 'D'];
const hat2 = ['1', '2', '3', '4'];
let prevCheck = null;
let count = 0;
const checker = () => {
return new Date().getSeconds() > 20;
};
const next = () => {
count++;
};
const reset = () => {
count = 0;
};
const render = (content) => {
// console.log(`clockInterval ${content}`); // debug output
document.querySelector(".main").textContent = content;
};
const fn = () => {
const check = checker();
const arr = check ? hat1 : hat2;
// empty arr skip
if (arr.length <= 0)
return;
// restart count to 0
if (prevCheck !== null && prevCheck !== check)
reset();
render(arr[count % arr.length]);
prevCheck = check;
next();
};
return setInterval(fn, 1000);
}
// start interval
const clock = setClockInterval();
// stop
clearInterval(clock);
You can greatly simplify your code by removing everything from inside the if block that is repeated and running it afterward. You also need to get a new Date on each iteration, otherwise the array won't change when it gets to the designated time.
I have changed the condition check to be odd/even seconds to show how it swaps between arrays without losing the index.
// Global, could be kept inside closure
let a = -1;
// Function to run each iteration
(function f() {
// OP code
// let hat = new Date().getHours() > 18? ['A','B','C','D'] : ['1','2','3','4'];
// faster iteration as example
let hat = new Date().getSeconds() % 2? ['A','B','C','D'] :['1','2','3','4'];
a = (a + 1) % hat.length;
document.querySelector(".main").textContent = hat[a];
setTimeout(f, 1000);
})();
<div class="main"></div>

JavaScript: async function that would retry for a period of time at a given interval

I am trying to write an async function that is going to await a function that got passed as one parameter and I wanted this async function to retry this operation for 5 mins every 10 seconds.
I found one function that sort of does this but it retries based on the numbers of times instead.
async function retry(fn, n) {
for (let i = 0; i < n; i++) {
try {
const ret = await fn();
if(!ret) throw new Error() // if `ret` is null or undefined, we will retry.
return ret
} catch {}
}
throw new Error(`Failed retrying ${n} times`);
}
Is there a way to tweak this function to satisfy my use cases?
Since your function is async, you can easily create timeouts to wait between subsequent calls:
const sleep = t => new Promise(r => setTimeout(r, t))
async function retry(fn) {
const startTime = Date.now()
while(Date.now() - startTime < 5 * 60 * 1000) {
try {
const ret = await fn();
if(ret)
return ret
// if `ret` is null or undefined, we won't return.
} catch {}
await sleep(10 * 1000)
}
throw new Error(`Failed retrying`);
}

How do I make a running total of a variable that changes in JavaScript due to live data?

I need to measure how long the following code calls the vibrate function in seconds and display the seconds which I have working. However, I don't know how to create a running total so that every time vibrate() is called, it both measures the length in seconds and then adds this to a running total.
function goodValue() {
startTime = new Date()
string1 = 'GOOD!!! :)'
displayValue('Angles', string1)
}
var startTime = {}
function badValue() {
var endTime = new Date()
vibrate1(1500)
string2 = 'BAD!!! :('
displayValue('Angles', string2)
var timeDiff = endTime - startTime //in ms
// strip the ms
timeDiff /= 1000
// get seconds
seconds = Math.round(timeDiff);
displayValue('TotalTime', + seconds);
}
This should now work. It's assuming that you call goodAngle() when the app recognizes a good angle, and badAngle() when the app recognizes a bad angle.
var totalTime = 0
var startTime = false
function goodAngle() {
if (startAngle) {
timeDiff = (Date.now() - startTime) / 1000
console.log('timeDiff: %s seconds', timeDiff)
totalTime += timeDiff
console.log('total timeDiff: %s seconds', timeDiff)
string1 = 'GOOD!!! :)'
displayValue('Angles', string1)
displayValue('BadPostureTime', Math.round(timeDiff))
startTime = false // So it can't be called again unless there's a badAngle first
}
}
function badAngle() {
if (!startTime) {
// Only set startTime if there isn't a current timer running
startTime = Date.now()
vibrate1(1500)
string2 = 'BAD!!! :('
displayValue('Angles', string2)
}
}
Skip to the expanded code snippet for the answer to this question. The rest of the examples address measuring other related metrics.
Measuring Each Individual Time
If you want to measure a function's execution time, you can wrap it in another function that uses console.time() and console.timeEnd():
function measure (fn, label = fn.name) {
return function () {
try {
console.time(label)
return fn.apply(this, arguments)
} finally {
console.timeEnd(label)
}
}
}
function delay (ms) {
const now = Date.now()
while (Date.now() - now < ms) { }
}
// wrap the function that needs to be tested
const timedDelay = measure(delay)
timedDelay(100)
timedDelay(10)
This measure() function has a few nice features:
The original function's return value is always preserved
The original function's thrown error is always preserved
If the original function throws an error, the time taken is still logged
The optional label argument is used to label the time that appears in the console
The label defaults to the name of the original function
The original function receives the context and arguments exactly as they were passed
To measure asynchronous functions, you can use the following, which can handle concurrent calls by incrementing a number used within the label in addition to the features above:
function measureAsync (fn, name = fn.name) {
let index = 0
return async function () {
const label = `${name} (${++index})`
try {
console.time(label)
return await fn.apply(this, arguments)
} finally {
console.timeEnd(label)
}
}
}
function delay (ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
// wrap the async function that needs to be tested
const timedDelay = measureAsync(delay)
timedDelay(1000)
timedDelay(100)
timedDelay(500)
Measuring Total Time
Using the above two implementations, you can modify them with the Performance API to measure cumulative time instead:
Synchronous
function measureTotal (fn, label = fn.name) {
return Object.assign(function measured () {
try {
performance.mark(label)
return fn.apply(this, arguments)
} finally {
performance.measure(label, label)
const [{ duration }] = performance.getEntriesByName(label, 'measure')
const total = measured.total += duration
performance.clearMarks(label)
performance.clearMeasures(label)
console.log(`${label}: ${total.toFixed(3)}ms`)
}
}, {
total: 0
})
}
function delay (ms) {
const now = Date.now()
while (Date.now() - now < ms) { }
}
// wrap the function that needs to be tested
const timedDelay = measureTotal(delay)
timedDelay(100)
timedDelay(10)
timedDelay(50)
console.log('total:', timedDelay.total)
Asynchronous
function measureTotalAsync (fn, name = fn.name) {
let index = 0
return Object.assign(async function measured () {
const label = `${name} (${++index})`
try {
performance.mark(label)
return await fn.apply(this, arguments)
} finally {
performance.measure(label, label)
const [{ duration }] = performance.getEntriesByName(label, 'measure')
const total = measured.total += duration
performance.clearMarks(label)
performance.clearMeasures(label)
console.log(`${label}: ${total.toFixed(3)}ms`)
}
}, {
total: 0
})
}
function delay (ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
// wrap the async function that needs to be tested
const timedDelay = measureTotalAsync(delay)
Promise.all([
timedDelay(1000),
timedDelay(100),
timedDelay(500)
]).finally(() => {
console.log('total:', timedDelay.total)
})
References
performance.mark()
performance.measure()
performance.getEntriesByName()
It is hard to tell what your code is supposed to do but I would advise making
a function which would wrap around your vibrate1 implementation.
The measureTime function takes a function as an argument (in your case vibrate) and
returns a function which has a property totalTime that records the execution
time. Note that it uses performance.now() instead of a Date object. See
https://developer.mozilla.org/fr/docs/Web/API/Performance/now for reference.
function measureTime(fn) {
const returnedFn = function measuredFunction (arg) {
const start = performance.now();
fn(arg);
const end = performance.now();
returnedFn.totalTime = returnedFn.totalTime + ((end - start) / 1000);
};
returnedFn.totalTime = 0;
return returnedFn;
}
const measuredVibrate = measureTime(vibrate1);
displayValue(measuredVibrate.totalTime);

Categories

Resources