I've been bashing my head against this wall I am completely new to JavaScript coming from c#
and I am completely baffled for my class I have to smooth out a simple code we made to count down from zero by making it into a loop and for the life of me I just cant get it to work
var i = 10;
var timeout = 10000;
var x = 10
if (i == 5) {
alert("help me")
}
while (i > 0) {
//10
setTimeout(() => {
document.getElementById("counter").innerHTML = i;
i = i - 1;
}, timeout);
timeout = timeout - 1000;
}
Asynchrony in JavaScript is a rather involved subject.
Traditional loops (for, while, do..while, for..of) are synchronous, and so cannot help you reach your goal here.
To do what you want, you can use indirect recursion like so:
const countdown = (from = 10, to = 0, interval = 1000, cb = console.log) => {
if(from < 0) return
cb(from)
setTimeout(() =>
countdown(--from), interval)
}
countdown()
There is also a more modern approach that enables the use of syntax that looks a bit more familiar. This approach uses for await... of, the syntax for which does not appear to be supported by StackOverflow's transpiler:
const delay = (interval) => new Promise((resolve) =>
setTimeout(resolve, interval))
async function* countdown(from = 10, to = 0, interval = 1000) {
for(;from >= 0; from--) {
yield from
await delay(interval)
}
}
for await(let count of countdown()) {
console.log(count)
}
setInterval is what you're looking for to execute a function every certain amount of time:
let i = 10;
let interval = setInterval(function() {
document.getElementById("counter").innerHTML = i;
i--;
if(i < 0) clearInterval(interval);//Clear the interval when complete
}, 1000);//Every 1000 ms = every second
The code below will countdown to zero.
var i = 10;
while (i > 0){
i--;
console.log(i);
}
Related
I'm trying to print one letter after another but this code just waits 100ms and then prints the value with no pauses. Any idea why this happens and how to fix it?
Code:
for (let i = 0; i < welcText.length; i++) {
setTimeout(()=>{
welcome.innerText += welcText[i];
},100);
}
Note that setTimeout does not actually delay the running of the code that comes after; it just schedules for something to happen in the future. So all the letters are added at the same time, after 100 milliseconds.
The most straightforward way to fix it is to make each subsequent timeout wait a bit longer, by multiplying the delay with the index of the letter (i * 100):
const welcome = document.getElementById('welcome');
const welcText = 'Welcome!';
for (let i = 0; i < welcText.length; i++) {
setTimeout(() => {
welcome.innerText += welcText[i];
}, i * 100);
}
<div id="welcome"></div>
The following function divtxt() returns a Promise. You can use it to send a txt to a div with a given id and let each letter appear in ms intervals. AND: you can build a chain of any number of follow-up actions with it:
function divtxt(id, txt, ms, wait = 0) {
const div = document.getElementById(id);
return new Promise((res, rej) => {
setTimeout(() => { // optional initial timeout, when wait>0
div.textContent = "";
const a = txt.split(""),
iv = setInterval(() => {
if (a.length)
div.textContent += a.shift();
else {
clearInterval(iv);
res(txt);
}
}, ms);
}, wait);
});
}
divtxt("welcome", "Hello world, this is my way of doing it! 🤩", 100)
.then(prevText => (console.log(prevText + ' is done.'),
divtxt("descr", "You can also chain this function with any number of consecutive actions."
+" Now: wait for 2 seconds ...", 100)))
.then(() => divtxt("welcome", "This promised-based approach REALLY lets you do it!! 👍🏻", 50, 2000))
.then(() => (console.log("ready?"),"Yes! I am REALLY DONE now! 😁"))
.then(console.log)
#welcome {
font-weight: 900
}
<div id="welcome"></div>
<div id="descr"></div>
You can do it using setInterval instead, and using an iterator to call the next char of the string every time. This is a bit more complicated, but has the advantage of not having multiple schedulers running if the string is too long. See the working example below.
const welcome = document.getElementById('welcome');
const welcomeText = 'Welcome!';
// returns a function that returns the next char in the string everytime its called
function createIterator(string) {
let currentIndex = 0;
return function() {
return string[currentIndex++];
}
}
// initializes the iterator with the text
let welcomeIterator = createIterator(welcomeText);
let welcomeInterval = setInterval(function() {
let nextChar = welcomeIterator();
// if we finish the string we clear the interval
if (!nextChar) {
return clearInterval(welcomeInterval);
}
// if the char exists, we append it to the div
welcome.innerText += nextChar;
}, 100);
<div id="welcome"></div>
you can do it like this also
const welcText = 'Welcome!';
let i = 0;
const interval = setInterval(() => {
if (i >= welcText.length) {
clearInterval(interval);
} else {
document.getElementById('helloword').innerHTML += welcText[i];
i++;
}
}, 100);
<h1 id="helloword"></h1>
It's beginner's question. I'm trying to understand setTimeout. I want to print each number after a delay of 2 seconds. However, I can only delay the whole function, not the numbers. Can anyone tell me how to do it?
function print() {
for (let i = 10; i >= 0; i--) {
console.log(i);
}
}
setTimeout(print, 2000);
You need to use the setTimeout inside the print method (inside the for), and give a different delay for each iteration.
function print() {
for (let i = 10; i >= 1; i--) {
setTimeout(() => {
console.log(i);
}, 2000 * (10 - i));
}
}
print();
Another approach is using setInterval. It is more natural for this task then setTImeout.
let i = 10;
const interval = setInterval(() => {
console.log(i);
i--;
if(i === 0){
clearInterval(interval);
}
}, 2000);
setTimeout here will delay all the function and if I get what you want to do is to print each number in 2s delay.
ill recommend using Promises and async function.
here is a solution with only setTimeout
function print(i){
console.log(i)
if(i>0){
setTimeout(()=>{print(i-1)},2000)
}
}
print(10)
Another way to do this by setInterval
let limit = 10
console.log('start')
let interval = setInterval(()=>{
console.log(limit--)
if(!limit){
clearInterval(interval)
console.log('end')
}
},2000)
With immediate start from 10 without 2 seconds wait.
let limit = 10
console.log('start')
const executeLogic = () =>{
console.log(limit--)
}
executeLogic()
let interval = setInterval(()=>{
executeLogic()
if(!limit){
clearInterval(interval)
console.log('end')
}
},2000)
I have a function called ajaxCall() which simple does the call on a server and returns a value from a feed.
What I wanted to do is to save the value from feed to firstInterval variable, wait 10 seconds, make the call again and save the value to secondInterval variable. And then animate the increasing of these numbers on the webpage. This is what we have so far:
setInterval(function(){
getAmounts()
}, 11000);
function getAmounts(){
firstInterval = ajaxCall();
setTimeout(() => {
secondInterval = ajaxCall();
animateValue('#bronze-price p', firstInterval, secondInterval, 10000)
}, 10000);
};
function animateValue(id, start, end, duration) {
start = parseInt(start);
end = parseInt(end);
var range = end - start;
var current = start;
var increment = end > start? 1 : -1;
var stepTime = Math.abs(Math.floor(duration / range));
var obj = document.querySelector(id);
var timer = setInterval(function() {
current += increment;
obj.innerHTML = current;
if (current == end) {
clearInterval(timer);
}
}, stepTime);
}
So the idea is to have a function which gets first interval, after 10 seconds it grabs the second interval and calls the animation. This all is wrapped into setInterval function so I could change the number smoothly repeatedly.
However I am pretty sure that thats not a very clean solution since its setInterval in setTimeout and this all is wrapped in another setInterval function. I am also getting this kind of warnings in the console with both functions:
[Violation] 'setInterval' handler took ms
What would be the best approach to follow up on this idea but optimize the code?
I think Promises and requestAnimationFrame makes this a lot easier to handle while also getting rid of setTimeout/setInterval. An example would be:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#Getting_a_random_integer_between_two_values_inclusive
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// fake api endpoint
const ajaxCall = () => new Promise(resolve => {
setTimeout(resolve, 400, 1000 + getRandomIntInclusive(0, 1000));
});
// simple animation helper using requestAnimationFrame
const animate = (from, to, duration, cb) => {
const start = performance.now();
return new Promise(resolve => {
requestAnimationFrame(function loop() {
const f = (performance.now() - start) / duration;
cb(from + Math.round(f * (to - from)));
f < 1.0
? requestAnimationFrame(loop)
: resolve();
});
});
};
// main
(async (interval) => {
// where to output
const dst = document.querySelector('#out');
// initial value
let prev = 0;
// wait for next value of ajaxCall
while (true) {
const next = await ajaxCall();
console.log(`animating: ${prev} -> ${next}`);
// animate from `prev` to `next`
await animate(prev, next, interval, current => {
dst.innerHTML = current;
});
prev = next;
}
})(10000);
<div id="out"></div>
I'm writing a sorting algorithm visualiser in javascript. So far the code is working, however there are performance issues.
I'm trying to make it so that swap function takes 1 ms (speed.value) to run. However, other than first 4 swaps every swap call on average takes 4 ms to resolve.The code looks something like this:
async function bubbleSort(arr, start, end) {
for (i = start; i < end; i++) {
for (j = start; j < end - i - 1; j++) {
if (arr[j].val > arr[j + 1].val) {
await swap(arr, j, j + 1);
}
}
j = 0;
}
}
async function swap(arr, a, b) {
var t3 = performance.now();
await delay(speed.value);
Bars_c.push({ a, b });
var temp = arr[a].val;
arr[a].val = arr[b].val;
arr[b].val = temp;
swapOnGraph(arr, a, b);
var t4 = performance.now();
console.log(t4 - t3);
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Without "await delay(speed.value)" each swap takes less than a millisecond to complete. And testing just that function shows that its working as intended outside of swap so delay() rewrites seem pointless. How can I change the code so that each swap takes 1 ms to run.
SetTimeout method is indeed the bottleneck .It can be proved by running this code:
var results = 0,
iterations = 1000,
i = 0;
function go() {
var fn = function () {
results += new Date().getTime() - d;
i += 1;
if (i < iterations) {
go();
} else {
finish();
}
},
d = new Date().getTime();
setTimeout(fn, 0);
}
Average time will depend on the browser and as Pointy sad it can reach up to 16 ms. For this question I found the solution on stack-exchange. As this is a visualisation project and not a timer suggested solution works great.
I was trying to implement a function, which is supposed to post measurement A every 5 sec for 10 times, and then post measurement B every 5 sec for a random amounts of time. And I want repeat this function forever as I was trying to implement a fake agent.
So I had the code:
let intervalId = null, repeat = 0;
while (true) {
intervalId = setInterval(() => {
if (repeat < 5) {
// post measurement A
repeat += 1;
}
else {
clearInterval(intervalId)
}
}, 1000);
repeat = 0;
intervalId = setInterval(() => {
if (repeat < Math.floor(Math.random() * 11)) {
// post measurement B
repeat += 1;
}
else {
clearInterval(intervalId)
}
}, 1000);
}
The two setInterval() function didn't happen consecutively as I expected, instead they happened at the same time. And the while (true) loop seems not behave as expected either. I'm just wondering is there any way to get around with this problem? Thanks.
You can create two function, one is doA() and one is doB().
Start with doA(), count the number of time //do A is called, when it reached 10, clearInterval and call doB().
In doB(), set the min and max time it should be called, then when it reached randTime clearInterval and doA()
function doA() {
let count = 0;
const a = setInterval(() => {
//do A
console.log('do A');
count += 1;
if (count === 10) {
clearInterval(a);
doB();
}
}, 5000/10);
}
function doB() {
// set your min and max for B
const minTime = 1;
const maxTime = 10;
const randTime = Math.floor(Math.random() * (maxTime - minTime + 1)) + minTime;
let count = 0;
const b = setInterval(() => {
// do B
console.log(randTime);
console.log('do B');
count += 1;
if (count === randTime) {
clearInterval(b);
doA();
}
}, 5000 / randTime);
}
doA();
Working on top of your code, first thing first, remove infinite while loop. It will run endlessly in synchronous fashion while setInterval is asynchronous. repeat value will be far ahead before you do repeat += 1.
Second, break them down in function so they have their own closure for intervalId and repeat value.
function intervalA () {
let intervalId = null
let repeat = 0
intervalId = setInterval(() => {
if (repeat < 5) {
console.log(new Date(), 'A')
// post measurement A
repeat += 1; // increment repeat in callback.
}
else {
clearInterval(intervalId); // done with interval, clear the interval
intervalB(); // and start interval B
}
}, 1000)
}
function intervalB () {
let repeat = 0
let randomEnd = Math.floor(Math.random() * 11) // calculate when it should end.
let intervalId = setInterval(() => {
if (repeat < randomEnd) {
console.log(new Date(), 'B will finish in', randomEnd, 'times')
repeat += 1
}
else {
clearInterval(intervalId) // clear the interval once done
}
}, 1000)
}
intervalA(); //start with interval A
Currently, the intervals are being set at once, synchronously, at the start of your script and during every while thereafter. It would probably be clearer if you only a single interval, with a variable that indicated which measurement to run, and change that variable every random-nth iteration:
const getRandomRepeats = () => Math.ceil(Math.random() * 11)
let repeatsRemaining = getRandomRepeats();;
let measureA = true;
setInterval(() => {
repeatsRemaining--;
if (repeatsRemaining === 0) {
repeatsRemaining = getRandomRepeats();
measureA = !measureA;
}
console.log('repeats remaining: ' + repeatsRemaining);
if (measureA) {
console.log('posting a');
} else {
console.log('posting b');
}
}, 1000);