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)
Related
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);
I am trying to make a counter that counts to big numbers such as 6 billion (like google's random number generator) but the browser freezes.
var counter = document.querySelector("#blahblahblah");
var nb = 0;
var ne = 6_000_000_000;
for (;nb<=ne;nb++) {
counter.innerHTML = nb;
};
The best thing you can do is use requestAnimationFrame. This represents the fastest rate that you can update the DOM.
Use the callback to update your element text to the number proportional to the time allowed; 3 seconds (a few) from your question title
const runCounter = (num, timeout, el) => {
let start;
const step = (ts) => {
if (!start) {
start = ts; // record the first iteration timestamp
}
const progress = (ts - start) / timeout; // how far into the time span
if (progress < 1) {
el.textContent = Math.floor(num * progress);
requestAnimationFrame(step); // request the next frame
} else {
el.textContent = num; // finish
}
};
requestAnimationFrame(step);
};
runCounter(6_000_000_000, 3000, document.getElementById("blahblahblah"));
<output id="blahblahblah">0</output>
I am new to JavaScript and was learning about promises. I want to print 5 to 0 numbers at the interval of 2 seconds each.
For example for the first time after 2 seconds print 5 and then after 2 second print 4 and so on.
I have written a code to do so but not able to get the correct output.
var count = 5;
var i = 2;
function d()
{
let p = new Promise((resolve,reject)=>{
while(count>=0)
{
setTimeout(resolve,i*1000,count);
i= i*2;
count = count-1;
//count = count-1;
}
});
return p;
}
d().then((x)=>{
console.log(x);
});
It is showing output as 5. Can anyone correct it?
It's much easier to write this using the async ... await syntax... you can then create a pause function that will delay by timeout milliseconds.
We can wrap this in a while statement, looping until the count is zero.
async function pause(timeout) {
return new Promise(resolve => setTimeout(resolve, timeout))
}
async function countDown(count) {
while (count > 0) {
await pause(2000);
console.log('count:', count);
count--;
}
}
countDown(5);
Whenever you are writing a program, it is a good idea to quantify what it is you need to keep track of.
In this case we have three components.
We want to do something on each "tick", and we want to know what "tick" we are on.
We want to count down from a specific number until we are at 0.
Each "tick" should be separated by a set amount of time.
A basic for..loop let's us loop over the number range and queue up some setTimeout calls with our callback:
/**
* CountDown
*
* #param {Function} callback - The callback is a function that we call each "tick"
* #param {number} countDownFrom - The countdown starts at this number
* #param {number} interval - the number of milliseconds between each "tick"
*/
function CountDown(
callback,
countDownFrom,
interval
) {
for (var count = 0; count <= countDownFrom; count++) {
setTimeout(callback, (countDownFrom - count) * interval, count);
}
}
CountDown(
(x) => {
console.log(x);
},
5,
2 * 1000
);
I have put some links to www.w3schools.com in this answer. There you can find documentation and tutorials for a lot of JavaScript problems.
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();
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);