How set interval() and clear interval() works under the hood - javascript

this is what I have done so far. The function for click on other buttons is to have an array filtered from data array and provide back and forth buttons functionality. the specific part about which i want to know is this piece of code. and could it be done more efficiently? i am sure its wrong but can't point why clearnInterval() doesn't work and i get an error after 4 seconds even though the array is done iterating.
mainBtns.forEach(mainBtn => {
mainBtn.addEventListener('click', () => {
mainBtn.style.textDecoration = 'underline';
//homeBtn
const setInt = null;
if(mainBtn.dataset.name === 'home'){
setInt = setInterval(() => {
i++;
targetTest.src = data[i].src;
targetTest.alt = data[i].alt;
console.log(targetTest);
functionalBtns(data);
if(i > length){
clearTimeout();
targetTest.src = data[0].src;
targetTest.alt = data[0].alt;
}
},4000);
}

Related

How to change value of global var when it is being used as a parameter in a function? (Javascript)

I want to be able to change the value of a global variable when it is being used by a function as a parameter.
My javascript:
function playAudio(audioFile, canPlay) {
if (canPlay < 2 && audioFile.paused) {
canPlay = canPlay + 1;
audioFile.play();
} else {
if (canPlay >= 2) {
alert("This audio has already been played twice.");
} else {
alert("Please wait for the audio to finish playing.");
};
};
};
const btnPitch01 = document.getElementById("btnPitch01");
const audioFilePitch01 = new Audio("../aud/Pitch01.wav");
var canPlayPitch01 = 0;
btnPitch01.addEventListener("click", function() {
playAudio(audioFilePitch01, canPlayPitch01);
});
My HTML:
<body>
<button id="btnPitch01">Play Pitch01</button>
<button id="btnPitch02">Play Pitch02</button>
<script src="js/js-master.js"></script>
</body>
My scenario:
I'm building a Musical Aptitude Test for personal use that won't be hosted online. There are going to be hundreds of buttons each corresponding to their own audio files. Each audio file may only be played twice and no more than that. Buttons may not be pressed while their corresponding audio files are already playing.
All of that was working completely fine, until I optimised the function to use parameters. I know this would be good to avoid copy-pasting the same function hundreds of times, but it has broken the solution I used to prevent the audio from being played more than once. The "canPlayPitch01" variable, when it is being used as a parameter, no longer gets incremented, and therefore makes the [if (canPlay < 2)] useless.
How would I go about solving this? Even if it is bad coding practise, I would prefer to keep using the method I'm currently using, because I think it is a very logical one.
I'm a beginner and know very little, so please forgive any mistakes or poor coding practises. I welcome corrections and tips.
Thank you very much!
It's not possible, since variables are passed by value, not by reference. You should return the new value, and the caller should assign it to the variable.
function playAudio(audioFile, canPlay) {
if (canPlay < 2 && audioFile.paused) {
canPlay = canPlay + 1;
audioFile.play();
} else {
if (canPlay >= 2) {
alert("This audio has already been played twice.");
} else {
alert("Please wait for the audio to finish playing.");
};
};
return canPlay;
};
const btnPitch01 = document.getElementById("btnPitch01");
const audioFilePitch01 = new Audio("../aud/Pitch01.wav");
var canPlayPitch01 = 0;
btnPitch01.addEventListener("click", function() {
canPlayPitch01 = playAudio(audioFilePitch01, canPlayPitch01);
});
A little improvement of the data will fix the stated problem and probably have quite a few side benefits elsewhere in the code.
Your data looks like this:
const btnPitch01 = document.getElementById("btnPitch01");
const audioFilePitch01 = new Audio("../aud/Pitch01.wav");
var canPlayPitch01 = 0;
// and, judging by the naming used, there's probably more like this:
const btnPitch02 = document.getElementById("btnPitch02");
const audioFilePitch02 = new Audio("../aud/Pitch02.wav");
var canPlayPitch02 = 0;
// and so on
Now consider that global data looking like this:
const model = {
btnPitch01: {
canPlay: 0,
el: document.getElementById("btnPitch01"),
audioFile: new Audio("../aud/Pitch01.wav")
},
btnPitch02: { /* and so on */ }
}
Your event listener(s) can say:
btnPitch01.addEventListener("click", function(event) {
// notice how (if this is all that's done here) we can shrink this even further later
playAudio(event);
});
And your playAudio function can have a side-effect on the data:
function playAudio(event) {
// here's how we get from the button to the model item
const item = model[event.target.id];
if (item.canPlay < 2 && item.audioFile.paused) {
item.canPlay++;
item.audioFile.play();
} else {
if (item.canPlay >= 2) {
alert("This audio has already been played twice.");
} else {
alert("Please wait for the audio to finish playing.");
};
};
};
Side note: the model can probably be built in code...
// you can automate this even more using String padStart() on 1,2,3...
const baseIds = [ '01', '02', ... ];
const model = Object.fromEntries(
baseIds.map(baseId => {
const id = `btnPitch${baseId}`;
const value = {
canPlay: 0,
el: document.getElementById(id),
audioFile: new Audio(`../aud/Pitch${baseId}.wav`)
}
return [id, value];
})
);
// you can build the event listeners in a loop, too
// (or in the loop above)
Object.values(model).forEach(value => {
value.el.addEventListener("click", playAudio)
})
below is an example of the function.
btnPitch01.addEventListener("click", function() {
if ( this.dataset.numberOfPlays >= this.dataset.allowedNumberOfPlays ) return;
playAudio(audioFilePitch01, canPlayPitch01);
this.dataset.numberOfPlays++;
});
you would want to select all of your buttons and assign this to them after your html is loaded.
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
const listOfButtons = document.getElementsByClassName('pitchButton');
listOfButtons.forEach( item => {
item.addEventListener("click", () => {
if ( this.dataset.numberOfPlays >= this.dataset.allowedNumberOfPlays ) return;
playAudio("audioFilePitch" + this.id);
this.dataset.numberOfPlays++;
});

Push function is not working properly localStorage Javascript vanilla

I am doing a time-to-click game as you can imagine the fastest time would be the first place. I just want to have 3 scores. and have it on localstorage but every time I start a new game the actual score resets its value and it doesnt generate other scores. the 3 scores should have as value 0. I tried to push them as arrays but push function is not working well. at this point I am stuck. I dont know what to do. If u may help me, I would be really grateful, thanks!
let times = Array.from({
length: 3
})
let interval2;
// Timer CountUp
const timerCountUp = () => {
let times = 0;
let current = times;
interval2 = setInterval(() => {
times = current++
saveTimes(times)
return times
}, 1000);
}
// Saves the times to localStorage
const saveTimes = (times) => {
localStorage.setItem('times', JSON.stringify(times))
}
// Read existing notes from localStorage
const getSavedNotes = () => {
const timesJSON = localStorage.getItem('times')
try {
return timesJSON ? JSON.parse(timesJSON) : []
} catch (e) {
return []
}
}
//Button which starts the countUp
start.addEventListener('click', () => {
timerCountUp();
})
// Button which stops the countUp
document.querySelector('#start_button').addEventListener('click', (e) => {
console.log('click');
times.push(score = interval2)
getSavedTimes()
if (interval) {
clearInterval(interval);
clearInterval(interval2);
}
})
This:
// Button which stops the countUp
document.querySelector('#start_button').addEventListener('click', (e) => {
Probably should be this:
// Button which stops the countUp
document.querySelector('#stop_button').addEventListener('click', (e) => {
...considering you're already attaching an event-handler to start's 'click' event in the previous statement, and no-one would use #start_button as the id="" of a stop button.

HTML element update event not called because of heavy JS function

I have a pretty CPU intensive operation running in a JS function. To indicate to the user when it starts and stops, I am trying to display a badge. Here's what the code looks like:
function updateView() {
console.log(1)
document.getElementById('app-status').className = "badge badge-danger";
document.getElementById('app-status').textContent = "Processing";
console.log(2)
setTimeout(myUpdateView(), 0)
console.log(5)
document.getElementById('app-status').className = "badge badge-success";
document.getElementById('app-status').textContent = "Ready"; console.log(6)
}
function myUpdateView() {
console.log(3)
updateFlightParameters();
// Get departure airport.
var departureAirportICAO = $("#DEPARTURE_AIRPORT").val();
if (airports[departureAirportICAO] === undefined) {
alert("Departure airport is incorrect.");
} else {
departureAirport = airports[departureAirportICAO];
}
// Get arrival airport.
var arrivalAirportICAO = $("#ARRIVAL_AIRPORT").val();
if (airports[arrivalAirportICAO] === undefined) {
alert("Arrival airport is incorrect.");
} else {
arrivalAirport = airports[arrivalAirportICAO];
}
// Create waypoints.
createWaypoint(departureAirport);
createWaypoint(arrivalAirport);
// Create path. THIS FUNCTION CALLS SOME OTHER ASYNC FUNCTIONS.
generatePolylines(flightWaypoints);
console.log(4)
}
The problem is that the app-status element never changes it's color or text. Upon clicking the button that calls updateView(), the page hangs (to do the processing) without changing the element.
Does the heavy processing function return anything? This seems like a good idea for a do-while statement.
const doSomethingCool(){
let trackingVariable = false;
do{
result = setInterval(massiveCompute, 100)
if(result === true){
trackingVariable = true;
}
} while (trackingVariable == false)
I would make sure the computer has time to update the screen:
function doHeavyProcessing() {
for (var i=0;i<1000000000;i++) ;
document.getElementById('app-status').className = "badge badge-success";
document.getElementById('app-status').textContent = "Ready";
}
function updateView() {
document.getElementById('app-status').className = "badge badge-danger";
document.getElementById('app-status').textContent = "Processing";
setTimeout(doHeavyProcessing,100)
}
updateView()
.badge-danger { color:red }
.badge-success { color:green }
<div id="app-status"></div>

javascript || Angular2/6: Calling setInterval multiple times but last timerId is not stopping even though clearInterval called

Requirement:
User scans multiple job numbers, for each job number , I need to
call one API and get the total job details and show it in a table
below the scanned text box.
User don't want to wait until API call finishes. He will scan continuously irrespective of details came or not.
What I have done:
I have taken one variable jobNumberList which stores all the job numbers the user scanned
I am continuously calling the API, with those job numbers.
When API, gives response , then I am adding to the table
I created one method called m1()
m1() =>
this method will compare the jobNumberList with the Table Rows data.
if any item not found in the table rows data when compare to jobNumberList data, then those are still loading jobs ( means API , not given response to those jobs )
I am using setInterval() to call m1() (time inteval = 1000 )
When jobNumberList and the Table rows data matches then I am cancelling the interval
Issue:
Even though I am stopping the polling, still the interval is executing. What will be the issue?
*
The issue is intermittent (not continuous). When I scan job numbers
fast, then its coming. Normal speed not coming.
*
My Code is as follows
// START INTERVAL
startPolling() {
var self = this;
this.isPollingNow = true;
this.poller = setInterval(() => {
let jobsWhichAreScannedButNotLoadedInsideTableStill = self.m1();
if (self.isPollingNow && jobsWhichAreScannedButNotLoadedInsideTableStill.length === 0) {
self.stopPolling();
self.isPollingNow = false;
}
}, 1000);
}
//STOP INTERVAL
stopPolling() {
var self = this;
setTimeout(function () {
window.clearInterval(self.poller); // OR //clearInterval(self.poller) //
}, 0);
}
REQUIREMENT
DEBUGGING IMAGES
The following code will create and auto cancellable interval when there is not more jobs to scan, this principle will do the trick that you need, please ask if you have any doubt.
Use the Clear Jobs button to remove jobs into the array that keep all the existing jobs
var globalsJobs = [1,2,3,4];
function scannableJobs () {
return globalsJobs;
}
function generateAutoCancellableInterval(scannableJobs, intervalTime) {
var timer;
var id = Date.now();
timer = setInterval(() => {
console.log("interval with id [" + id + "] is executing");
var thereUnresponseJobs = scannableJobs();
if (thereUnresponseJobs.length === 0) {
clearInterval(timer);
}
}, intervalTime)
}
const btn = document.getElementById('removejobs');
const infobox = document.getElementById('infobox');
infobox.innerHTML = globalsJobs.length + ' jobs remainings'
btn.addEventListener('click', function() {
globalsJobs.pop();
infobox.innerHTML = globalsJobs.length + ' jobs remainings'
}, false);
setTimeout(() => generateAutoCancellableInterval(scannableJobs, 1000), 0);
setTimeout(() => generateAutoCancellableInterval(scannableJobs, 1000), 10);
<button id="removejobs">
clear jobs
</button>
<div id="infobox">
</div>
</div>

How to call async function to use return on global scope

I'm currently struggling with a function call, when I call the function from an if statement it does work but when I call it from outside it doesn't, my if statement only checks which button was pressed but I'm trying to remove the function from the button and just call it as soon as my app starts.
We will look at fetchJokes() inside jokeContainer.addEventListener('click', event => {
This is my current code:
const jokeContainer = document.querySelector('.joke-container');
const jokesArray = JSON.parse(localStorage.getItem("jokesData"));
// Fetch joke count from API endpoint
async function sizeJokesArray() {
let url = 'https://api.icndb.com/jokes/count';
let data = await (await fetch(url)).json();
data = data.value;
return data;
}
// use API endpoint to fetch the jokes and store it in an array
async function fetchJokes() {
let url = `https://api.icndb.com/jokes/random/${length}`;
let jokesData = [];
let data = await (await fetch(url)).json();
data = data.value;
for (jokePosition in data) {
jokesData.push(data[jokePosition].joke);
}
return localStorage.setItem("jokesData", JSON.stringify(jokesData));;
}
const jokeDispenser = (function() {
let counter = 0; //start counter at position 0 of jokes array
function _change(position) {
counter += position;
}
return {
nextJoke: function() {
_change(1);
counter %= jokesArray.length; // start from 0 if we get to the end of the array
return jokesArray[counter];
},
prevJoke: function() {
if (counter === 0) {
counter = jokesArray.length; // place our counter at the end of the array
}
_change(-1);
return jokesArray[counter];
}
};
})();
// pass selected joke to print on html element
function printJoke(joke) {
document.querySelector('.joke-text p').textContent = joke;
}
sizeJokesArray().then(size => (length = size)); // Size of array in response
jokeContainer.addEventListener('click', event => {
if (event.target.value === 'Fetch') {
fetchJokes(length);
} else if (event.target.value === 'Next') {
printJoke(jokeDispenser.prevJoke(jokesArray));
} else if (event.target.value === 'Prev') {
printJoke(jokeDispenser.nextJoke(jokesArray));
}
});
And I'm trying to do something like this:
// pass selected joke to print on HTML element
function printJoke(joke) {
document.querySelector('.joke-text p').textContent = joke;
}
sizeJokesArray().then(size => (length = size)); // Size of array in response
fetchJokes(length);
jokeContainer.addEventListener('click', event => {
if (event.target.value === 'Next') {
printJoke(jokeDispenser.prevJoke(jokesArray));
} else if (event.target.value === 'Prev') {
printJoke(jokeDispenser.nextJoke(jokesArray));
}
});
By the way, I'm aware that currently, you can't actually iterate through the array elements using prev and next button without refreshing the page but I guess that will be another question.
Couldn't think of a better title.(edits welcomed)
Async functions are, as the name implies, asynchronous. In
sizeJokesArray().then(size => (length = size)); // Size of array in response
fetchJokes(length);
you are calling fetchJokes before length = size is executed because, as you may have guessed, sizeJokesArray is asynchronous.
But since you are already using promises the fix is straightforward:
sizeJokesArray().then(fetchJokes);
If you have not fully understood yet how promises work, maybe https://developers.google.com/web/fundamentals/getting-started/primers/promises helps.

Categories