How do I switch the running codes using the IF? - javascript

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>

Related

Store value in a variable

I am new in Javascript. I develop a program that store the score of humun and computer guess number. I want the human score and computer score will update when I call the updateScore() functions. However, it works but the score unable to increase by last score.
Here is the code:
let humanScore = 0;
let computerScore = 0;
let currentRoundNumber = 1;
// Write your code below:
const generateTarget = () => {
return Math.floor(Math.random()*10);
};
const compareGuesses = () => {
// Humun & Computer guess a number
const humunGuess = 1;
const computerGuess = 2;
// Call the generateTarget functions
const secretTargetNumber = generateTarget();
// Compare the difference between Target number and humun guess number
const humunTarget = Math.abs(humunGuess - secretTargetNumber);
// Compare the difference between Target number and computer guess number
const computerTarget = Math.abs(computerGuess - secretTargetNumber);
// Return true if humun won, false if computer won
if (humunTarget < computerTarget || humunTarget == computerTarget) {
return true;
}
else {
return false;
}
};
let updateScore = () => {
switch (compareGuesses()) {
case true:
return humanScore+=1;
case false:
computerScore+=1;
}
};
updateScore()
console.log(humanScore)
console.log(computerScore)
It is a programming language based on javascript event/trigger features. all variables are reset when you call the file again.
The variables seem to be reset every time you call the javascript file
let humanScore = 0,
computerScore = 0,
currentRoundNumber = 1;
// Write your code below:
const generateTarget = () => {
return Math.floor(Math.random() * 10);
};
const compareGuesses = () => {
// Humun & Computer guess a number
const humanGuess = 1;
const computerGuess = 2;
// Call the generateTarget functions
const secretTargetNumber = generateTarget();
// Compare the difference between Target number and humun guess number
const humanTarget = Math.abs(humanGuess - secretTargetNumber);
// Compare the difference between Target number and computer guess number
const computerTarget = Math.abs(computerGuess - secretTargetNumber);
// Return true if humun won, false if computer won
if (humanTarget < computerTarget) {
return true;
} else {
return false;
}
};
let updateScore = () => {
switch (compareGuesses()) {
case true:
return humanScore += 1;
case false:
computerScore += 1;
}
};
let showScore = () => {
updateScore();
console.log(humanScore)
console.log(computerScore)
}
<button onclick="showScore()">Click</button>
Woking Demo : https://jsfiddle.net/6v25y9qd/
Push click and show console
Because you're new, and I think #Onur Özkır is on to something, I will give some guidelines instead.
I understand that you mostly trying out to see if things works, and will refactor afterwards, but I recommend that you create methods that does only one thing. You already done that with generateTarget(), but if I look at compareGuesses(), that method doesn't just compare guesses. It also reads and generates numbers. I would instead read the values in updateScore() and then add them as parameters in compareGuesses.
By restricting your methods to only do one thing will make your code more readable, your methods will be smaller so they are easier to grasp, and the code is easier to debug.
I would also like to suggest that you only return a value at the end of the method, and always return (unless you can fit everything on one row) using a variable.
I also don't like switches in javascript, because the forced use of return. Would rather use ifs statement or a shorthand if statements, using conditional/ternary operator.
Use method names and variables to explain the code (ex. the constant HUMAN_WON in the code below). Try to avoid comments as far as possible. Comments should, IMHO, only be used if you generate a documentation. I suggest that you get your hands on Clean Code, which was a revelation for me to read, even as an experienced programmer.
I will refactor your code as a suggestion of how it can look like:
let humanScore = 0;
let computerScore = 0;
let currentRoundNumber = 1;
const HUMAN_WON = 1, COMPUTER_WON = -1, EQUAL = 0;
const generateTarget = () => {
return Math.floor(Math.random() * 10);
};
const readHumanGuess = () => {
return 1; // replace with appropriate code
}
const generateComputerGuess = () => {
return 2; // replace with generateTarget();
}
const compareGuesses = (humanGuess, computerGuess, secretTargetNumber) => {
let result = EQUAL;
const humanTarget = Math.abs(humanGuess - secretTargetNumber);
const computerTarget = Math.abs(computerGuess - secretTargetNumber);
if (humanTarget < computerTarget) {
result = HUMAN_WON;
} else if (computerTarget < humanTarget) {
result = COMPUTER_WON;
}
return result;
};
let updateScore = () => {
let humanGuess = readHumanGuess();
let computerGuess = generateComputerGuess();
let secretTargetNumber = generateTarget();
let whoWon = compareGuesses(humanGuess, computerGuess, secretTargetNumber);
if (whoWon == HUMAN_WON) {
humanScore++;
} else if (whoWon == COMPUTER_WON) {
computerScore++;
}
};
let displayCurrentScore = () => {
updateScore();
console.log(`${humanScore} vs ${computerScore}`);
}
<input type="button" onclick="displayCurrentScore()" value="Display Score">
You can even go one step further refactoring readGuesses() from updateScore() and separate updating UI—displayCurrentScore()—from handling logic in updateScore().
let humanScore = 0;
let computerScore = 0;
let currentRoundNumber = 1;
const HUMAN_WON = 1, COMPUTER_WON = -1, EQUAL = 0;
const generateTarget = () => {
return Math.floor(Math.random() * 10);
};
const readHumanGuess = () => {
return 1; // replace with appropriate code
}
const generateComputerGuess = () => {
return 2; // replace with generateTarget();
}
const readGuesses = () => {
let humanGuess = readHumanGuess();
let computerGuess = generateComputerGuess();
let secretTargetNumber = generateTarget();
return [humanGuess, computerGuess, secretTargetNumber]; // returning array
}
const compareGuesses = (humanGuess, computerGuess, secretTargetNumber) => {
let result = EQUAL;
const humanTarget = Math.abs(humanGuess - secretTargetNumber);
const computerTarget = Math.abs(computerGuess - secretTargetNumber);
if (humanTarget < computerTarget) {
result = HUMAN_WON;
} else if (computerTarget < humanTarget) {
result = COMPUTER_WON;
}
return result;
};
let updateScore = (humanGuess, computerGuess, secretTargetNumber) => {
let whoWon = compareGuesses(humanGuess, computerGuess, secretTargetNumber);
if (whoWon == HUMAN_WON) {
humanScore++;
} else if (whoWon == COMPUTER_WON) {
computerScore++;
}
return {'human': humanScore, 'computer': computerScore};
};
let displayCurrentScore = () => {
let [humanGuess, computerGuess, secretTargetNumber] = readGuesses();
let score = updateScore(humanGuess, computerGuess, secretTargetNumber);
console.log(`${score.human} vs ${score.computer}`);
}
<input type="button" onclick="displayCurrentScore()" value="Display Score">

Problem with Animating Number Counter on Javascript

I was hoping to create a counter which would increment to the eventual number of followers. I have other code, but I just included the part that would affect the incrementation. Currently, when I run the program the HTML file only displays the final number of followers, and the word "Followers" showing it is able to get the proper number, but it isn't able to have an incrementation animation as I would like. How would I go about fixing this?
var text_field = document.getElementById('followers');
fetch(api)
.then((response) => {
return response.json();
})
.then((data) => {
var number = data['Count'][0][0];
const updateCount = () => {
const target = number;
const count = text_field.innerText;
const increment = target / speed;
if (count < target) {
text_field.innerText = count + increment;
setTimeout(updateCount, 5);
} else {
text_field.innerText = target + ' Followers';
}
};
updateCount();
});
The innerText property returns a string value. Use the parseInt function before any calculations.
var text_field = document.getElementById("followers");
function counter(){
var number = 100;
const updateCount = () => {
const target = number;
const count = parseInt(text_field.innerText); //parsing
const speed=number;
const increment = target / speed;
if (count < target) {
text_field.innerText = count + increment;
setTimeout(updateCount, 5);
} else {
text_field.innerText = target;
}
};
updateCount();
}
counter();
<div id="followers">1</div> Followers!!!

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>

Creating a variable counter in javascript with variable speed

I'm having a problem.
I want to make a counter that counts from 1 to 9 and repeats.
The time between the counts should be variable (1 to 10 seconds in the same series).
Sometimes, it should add 1, sometimes 2 (so sometimes skip one number).
Is that possible with javascript?
Thank you in advance.
This is the code I have, but is only counts, does not skip a number sometimes and the time to count is fixed at 500 ms.
<div id="value">1</div>
<script>
function animateValue(id){
var obj = document.getElementById(id);
var current = obj.innerHTML;
setInterval(function(){
obj.innerHTML = current++;
},500);
}
animateValue('value');
</script>
</html>````
First, a JSFiddle:
http://jsfiddle.net/5k0xsrj6/embedded/result/
JSFiddle with larger, stylized text:
https://jsfiddle.net/9f4vgLbx/embedded/result
Edit: I see you're not familiar with JavaScript. I've included non-ES6 JavaScript as well.
The biggest issue you'll face with your code is the use of setInterval, as you want a variable timer.
Instead of setInterval, consider a function that calls itself and sets a timer. Once the setTimeout is called, it will invoke the function again to set another timeout, effectively creating an interval.
Non ES6 Script:
var el = document.body;
var max_count = 9;
var current_count = 1;
// Function which sets our timer
function timer(delay) {
// Set a timeout with our passed `delay` arg
setTimeout(function () {
// Adds either 1 or 2 based on the return value of getIteration
current_count += getIteration();
// As we have a max, reset to 1 if we're over
if (current_count > max_count) {
current_count = 1;
}
// Update innerHTML
writer();
// Call next iteration
loop();
}, delay);
}
// Writes our innerHTML
function writer() {
el.innerHTML = current_count;
}
// Returns 1000 through 10000
function getDelay() {
return Math.ceil(Math.random() * 10) * 1000;
}
// Returns either 1 or 2
function getIteration() {
return Math.ceil(Math.random() * 2);
}
// Our main function to loop
function loop() {
// getDelay will return a value between 1000 - 10000
timer(getDelay());
}
// Sets Initial Value
writer();
// Main
loop();
Original:
Here's an example of the code on the JSFiddle. I've included comments to hopefully explain the logic.
{
const el = document.body;
const max_count = 9;
let current_count = 1;
// Function which sets our timer
const timer = delay => {
setTimeout(() => {
current_count += getIteration();
if (current_count > max_count) {
current_count = 1;
}
// Update innerHTML
writer();
// Call next iteration
main();
}, delay);
}
// Writes our innerHTML
const writer = (str, log) => {
if (log) {
console.log(str);
} else {
el.innerHTML = `Current count: ${current_count}`;
}
}
// Returns 1000 through 10000
const getDelay = () => {
return Math.ceil(Math.random() * 10) * 1000;
}
// Returns either 1 or 2
const getIteration = () => {
return Math.ceil(Math.random() * 2);
}
// Our main function to loop
const main = () => {
const delay = getDelay();
writer(`Next delay is ${delay}ms`, true);
timer(delay);
}
// Set Initial Value
writer();
// Main
main();
}
Hope this helps! If you have any questions, feel free to ask!

Javascript, calling asynchronous function in a for loop [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I wish to call an asynchronous function in a for loop. I am having significant trouble doing so and am getting a variety of errors such as undefined variables and such.
Evaluator.prototype.asyncEval = function(predictor) {
let self = this;
let metric = 0; //METRICS SHOULD BE UPDATED BY ASYNC FUNCTION
for (let i = 1; i < this.fullTraces.length; i++) {
(function(index){
let deltaTime = self.fullTraces[i][2] - this.fullTraces[i-1][2];
let subTraces = self.fullTraces.slice(0, i);
predictor.predict(subTraces, (dist) => { // ASYNC FUNCTION
if (dist !== null) {
let result = dist.getTopK(1);
let pX = result[0][0][0];
let pY = result[0][0][1];
let x = self.fullTraces[i][0];
let y = self.fullTraces[i][1];
let a = pX - x;
let b = pY - y;
metric += Math.sqrt(a*a + b*b);
}
});
}(i));
}
metric /= this.fullTraces.length - 1;
return metric;
}
My asynchronous function predictor.predict() is actually using a POST request to get results from my web server.
YourPredictor.prototype.predict = function(trace, callback) {
return asyncPostRequest('https://0.0.0.0:5000/prediction', trace, responseText => {
prediction = JSON.parse(responseText);
let pred = [prediction['xs'], prediction['ys'], 'm'];
let dist = Dist.NaiveDistribution.from(pred, mouseToKey);
dist.set(pred, 1);
callback(dist);
});
}
How can I get this to work? I am running this on Chrome. I know there is the new await and async from ES7, but I don't want to use something that bleeding edge yet.
You need to refactor the code to replace the loop with a self-invoking loop, so that each time the asynchronousis called, the result from it is handed back, then iteration is checked, if i
Since the main code is asynchronous you will also need a callback for the initial call function (the doneCallback below).
Example
I left in the original code where it is expected to work, but made a couple of changes for it to work here.
function Evaluator() {}; // dummy for test
Evaluator.prototype.asyncEval = function(predictor, doneCallback) {
let self = this;
let metric = 0;
let length = 10; //this.fullTraces.length;
let i = 1;
// replaces for-loop
(function loop() {
self.predict(0, (dist) => {
// ...
metric += dist;
if (++i < length) loop();
else {
// ...
doneCallback(metric);
}
});
})();
}
// note: I changed prototype parent in this example
Evaluator.prototype.predict = function(trace, callback) {
//...
setTimeout(callback, 100, Math.random() * 100); // simulate async call
}
// TEST
var test = new Evaluator();
test.asyncEval(0, function(result) {
document.querySelector("div").innerHTML = result;
});
<div>Calcing...</div>
Example leaving the original code in place at the intended locations:
function Evaluator() {}; // dummy for test
Evaluator.prototype.asyncEval = function(predictor, doneCallback) {
let self = this;
let metric = 0; //METRICS SHOULD BE UPDATED BY ASYNC FUNCTION
let length = 10; //this.fullTraces.length;
let i = 1;
// replaces for-loop
(function loop() {
//let deltaTime = self.fullTraces[i][2] - this.fullTraces[i - 1][2];
//let subTraces = self.fullTraces.slice(0, i);
self.predict(0, (dist) => { // ASYNC FUNCTION
//predictor.predict(subTraces, (dist) => { // ASYNC FUNCTION
/*if (dist !== null) {
let result = dist.getTopK(1);
let pX = result[0][0][0];
let pY = result[0][0][1];
let x = self.fullTraces[i][0];
let y = self.fullTraces[i][1];
let a = pX - x;
let b = pY - y;
metric += Math.sqrt(a * a + b * b);
}*/
metric += dist;
if (++i < length) loop();
else {
//metric /= this.fullTraces.length - 1;
//return metric; <- don't use, instead use:
doneCallback(metric);
}
});
})();
}
// note: I changed prototype parent in this example
Evaluator.prototype.predict = function(trace, callback) {
setTimeout(callback, 100, Math.random() * 100); // simulate async call
/*return asyncPostRequest('https://0.0.0.0:5000/prediction', trace, responseText => {
prediction = JSON.parse(responseText);
let pred = [prediction['xs'], prediction['ys'], 'm'];
let dist = Dist.NaiveDistribution.from(pred, mouseToKey);
dist.set(pred, 1);
callback(dist);
});*/
}
// TEST
var test = new Evaluator();
test.asyncEval(0, function(result) {
document.querySelector("div").innerHTML = result;
});
<div>Calcing...</div>
If you dont want to use async + await combination, I would suggest to take a look at this post.
Asynchronous for cycle in JavaScript
I'm using this asyncLoop function and it's working great:
The function takes three arguments: 1) iterations, 2) a loop callback function and 3) Done callback function,
Check out the code:
function promise1(param){
return new Promise((resolve, reject) => setTimeout(() => { resolve(`Promise1 ${param} Done`)}, 2000))
}
function asyncLoop(iterations, func, callback) {
var index = 0;
var done = false;
var loop = {
next: function() {
if (done) {
return;
}
if (index < iterations) {
index++;
func(loop);
} else {
done = true;
callback();
}
},
iteration: function() {
return index - 1;
},
break: function() {
done = true;
callback();
}
};
loop.next();
return loop;
}
var asyncProc = ["Process1", "Process2", "Process3"]
asyncLoop
(
asyncProc.length,
(loop) => { promise1(asyncProc[loop.iteration()]).then((msg) =>{ console.log(msg); loop.next() }) },
() => { console.log("ALL DONE!")});
You cannot return the value of the "metric" synchronously if it is being modified asynchronously. You'll need to pass a callback into your method so the "metric" can be returned when it is ready.
Evaluator.prototype.asyncEval = function (predictor, callback) {
let self = this;
let metric = 0; //METRICS SHOULD BE UPDATED BY ASYNC FUNCTION
let callbacks = 0; // Keep a counter for the asynchronous callbacks
for (let i = 1; i < self.fullTraces.length; i++) {
let deltaTime = self.fullTraces[i][2] - this.fullTraces[i - 1][2];
let subTraces = self.fullTraces.slice(0, i);
// Queue up an asynchronous callback
predictor.predict(subTraces, (dist) => { // ASYNC FUNCTION
if (dist !== null) {
let result = dist.getTopK(1);
let pX = result[0][0][0];
let pY = result[0][0][1];
let x = self.fullTraces[i][0];
let y = self.fullTraces[i][1];
let a = pX - x;
let b = pY - y;
metric += Math.sqrt(a * a + b * b);
}
// Decrement the counter and check if we're done
if (--callbacks === 0) {
callback(metric / (self.fullTraces.length - 1));
}
});
// Increment the counter
callbacks++;
}
};

Categories

Resources