Why is the timer not running in my JavaScript code? [duplicate] - javascript

This question already has answers here:
Can JavaScript function execution be interrupted?
(3 answers)
Closed 14 hours ago.
I'm trying to add a timer to a JavaScript function that calculates the factors of a number. I want to log the number of elapsed seconds every second till the function returned. Here's my code:
const getFactors = N => {
return new Promise((resolve, reject) => {
let cnt = 0;
for (let index = 1; index <= N; index++) {
if (N % index === 0) cnt++;
}
resolve(cnt);
});
}
const logSeconds = () => {
const elapsedTime = Math.floor((Date.now() - startTime) / 1000);
console.log(`Elapsed time: ${elapsedTime} seconds.`);
}
let startTime = Date.now();
const loggingInterval = setInterval(logSeconds, 1000);
getFactors(10000000000).then(cnt => {
console.log(cnt);
clearInterval(loggingInterval);
}).catch(err => {
console.error(err);
});
However, asynchronous programming with Promises or async/await the timer does not seem to be working, and I'm passing a value of 10^9. The function runs correctly and logs the number of factors, but the timer does not log anything. What am I doing wrong?
It is important to note that this is not about optimizing the code for factors.
I'd appreciate any help or suggestions. Thank you!

function getFactors(N) {
let cnt = 0;
for (let index = 1; index <= N; index++) {
if (N % index === 0) cnt++;
}
return cnt;
}
function logSeconds(startTime) {
const elapsedTime = Math.floor((Date.now() - startTime) / 1000);
console.log(`Elapsed time: ${elapsedTime} seconds.`);
}
function calculateFactorsWithTimer(N) {
let startTime = Date.now();
const loggingInterval = setInterval(() => logSeconds(startTime), 1000);
const factors = getFactors(N);
clearInterval(loggingInterval);
return factors;
}
console.log(calculateFactorsWithTimer(10000000000)); // call with value of 10^9
try it

Related

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>

How do I Sum Array elements in every second

I'm trying to sum up the array of elements by every seconds. My array has 20 elements, and it should be summed up in 20seconds.
I'm using setTimeout in for loop but its not working , the loop finish before the first second. Anyway to achieve ?
for (var o = 0; o < 20; o++) {
setTimeout(function () {
tempPsum += array[o];
}, 1000);
}
Right, the loop finishes before the first setTimeout callback occurs. Your code schedules 20 callbacks that all occur, one after another, a second later.
You have two choices:
Schedule each one a second later than the previous one, or
Don't schedule the next one until the previous one finishes
#1 is simpler, so let's do that:
for (let o = 0; o < 20; o++) {
// ^^^−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
setTimeout(function () {
tempPsum += array[o];
}, 1000 * o);
// ^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−
}
By multiplying the delay time by o, we get 0 for the first timer, 1000 for the second, 3000 for the third...
Note the change from var to let. That way, there's a separate o for the callback to close over for each loop iteration. (See answers here, or...Chapter 2 of my new book. :-) )
If you can't use ES2015+ features in your environment, you can use the extra arguments to setTimeout instead:
for (var o = 0; o < 20; o++) {
setTimeout(function (index) {
// ^^^^^−−−−−−−−−−−
tempPsum += array[index];
// ^^^^^−−−−−−−−−−−
}, 1000 * o, o);
// ^^^^−−^−−−−−−−−−−−−−−−−−−−−−−−
}
That tells setTimeout to pass the third argument you give it to the callback as its first argument.
Ideal use case of setTimeout() is when you have to perform an operation once after a specific time frame. The scenario mentioned above requires same operation that is adding array element after a fixed time frame, so I believe setInterval() would be a better option.
var array = [1,2,3,4,5];
var tempSum = 0, i = 0;
var intrvl = setInterval( ()=> {
tempSum += array[i++];
console.log(tempSum)
if(i === array.length)
clearInterval(intrvl)
}, 1000)
This answer doesn't explain what the issue is, but offers an alternative to the other solutions. Instead of modifying the timeout using the index, you could add a sleep function that resolves a promise in a given amount of time.
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
Then loop through your array using for...of and sleep each iteration for a given amount of time.
let sum = 0;
for (const number of numbers) {
await sleep(1000);
sum += number;
}
This does require the code to be placed in an async function.
const randomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const numbers = Array.from({length: 20}, () => randomInt(10, 99));
console.log("numbers =", ...numbers);
(async () => {
let sum = 0;
for (const number of numbers) {
await sleep(1000);
console.log(sum, "+", number, "=", sum += number);
}
})();
#TJCrowder has said why your version doesn't work.
I will suggest another solution:
Another way: use setInterval
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
let tempPsum = 0
let i = 0
let intervalId = setInterval(function () {
tempPsum += numbers[i];
i = i + 1;
console.log(tempPsum);
if (i == 20) {clearInterval(intervalId)}
}, 1000);
I think you can use this approach:
var i = 0;
var arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var sum = 0;
function myLoop() {
setTimeout(() => {
sum = sum + arr[i];
console.log(sum);
i++;
if (i < 20) {
myLoop();
}
}, 1000)
}
myLoop();

How am I able to add timing to javascript that alters text every second?

I would like this code to count up from 0 to 940 (very fast) and alter the text every time it updates
Here's my code (inside my head tag):
<script type="text/javascript">
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
function onLoad(){
var x = document.getElementById("numberID");
var n = 940;
var text = "";
for(i = 0;i < n + 1;i++){
text = i;
x.innerHTML = text;
sleep(1);
}
}
</script>
At the moment, it just waits a second then displays '940' on screen and doesn't display it counting up.
Any help would be appreciated, thanks!
Here's the code I recently put in, still doesn't work:
const x = document.getElementById("numberID");
function newFrame(duration, start = performance.now()) {
requestAnimationFrame((now) => {
const elapsed = now - start;
x.innerText = Math.max(0, Math.min(duration,
Math.round(elapsed)));
if(elapsed < duration)
newFrame(duration, start);
})
}
}
newFrame(940);
Using a while loop to "sleep" is going to block the page's thread and nothing else can happen in the meantime. This is considered bad practice.
setTimeout guarantees that at least the defined time has passed, but can take (much) longer. This is imprecise, and especially bad for shorter intervals. Same with setInterval. They're also not recommended for callbacks that involve updating the DOM.
What you need to do is use a requestAnimationFrame.
function newFrame(duration, start = performance.now()) {
requestAnimationFrame((now) => {
const elapsed = now - start
console.log(`time passed: ${elapsed} ms`)
if(elapsed < duration)
newFrame(duration, start)
})
}
newFrame(940)
In your specific case, I'd replace the console.log statement put there for didactic purposes, with something along the lines of:
x.innerText = Math.max(0, Math.min(duration, Math.round(elapsed)))
Here's what that would look like:
const x = document.getElementById("numberID")
function newFrame(duration, start = performance.now()) {
requestAnimationFrame((now) => {
const elapsed = now - start
x.innerText = Math.max(0, Math.min(duration, Math.round(elapsed)))
if(elapsed < duration)
newFrame(duration, start)
})
}
newFrame(940)
<span id="numberID"></span>
The sleep function is not doing anything, what you need is a setTimeout to display the text at every x milliseconds.
Something like the below will work.
let x = null;
let timeout = null;
const changeText = (text) => {
x.innerHTML = text;
clearTimeout(timeout);
}
function onLoad() {
x = document.getElementById("numberID");
const n = 940;
const t = .01; // in seconds
for( let i = 0; i <= n; i++) {
timeout = setTimeout( () => changeText((i+1).toString()), (t * i) * 1000);
}
}
onLoad();
<span id="numberID"></span>

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

Categories

Resources