Not executing setInterval every time - javascript

I am making a stopwatch.
I want to display the interval between the time the start button is pressed and now in a p tag at any moment.
I tried this:
watch() {
const p = document.createElement("p");
p.id = "p";
document.body.appendChild(p);
document.getElementById("p")!.innerHTML = this.stop();
}
start() {
if(this.status === 'started') throw new Error('already started');
this.currentTime = Date.now();
this.interVal = setInterval(() => this.watch(), 100);
this.status = 'started';
return this.interVal;
}
stop() {
if(this.status === 'stopped') throw new Error('already stopped');
this.duration = Date.now() - this.currentTime + this.duration;
console.log(this.duration);
this.status = 'stopped';
return this.duration;
}

That setInterval() is repeatedly calling the this.watch() method, which subsequently calls the this.stop() method each time.
In the this.stop() method, you always set this.status = 'stopped' before returning. However, at the beginning of the method, you also check that if the status is equal to 'stopped', then you throw an Error. As a result, this.stop() can only be called once, because the first invocation sets this.status to 'stopped', which causes an Error to be thrown on every following invocation. And since this.watch() doesn't "catch" any thrown errors, the error will continue to bubble up, and this.watch() never finishes executing, therefore never updating the innerHTML.
I'm not entirely sure what your goal is, but to prevent the error from occurring, remove the line if(this.status === 'stopped') throw new Error('already stopped'); from this.stop().

Related

Nodejs setInterval and run first immediatly not after interval

I try to implement a loop in my noejs app that will always wait between the tasks. For this I found the setInterval function and I thought it is the solution for me. But as I found out, the first Interval, means the very first action also wait until the interval is ready. But I want that the first action runs immediatly and then each action with the given interval.
In arry scope:
myArray[0] starts immediatly while myArray[1..10] will start with Interval waiting time.
I tried it with:
function rollDice(profilearray, browserarray, url) {
return new Promise((resolve, reject) => {
var i = 0;
const intervalId = setInterval(
(function exampleFunction() {
console.log(profilearray[i].profil);
//########################################################################
createTestCafe("localhost", 1337, 1338, void 0, true)
.then((tc) => {
testcafe = tc;
runner = testcafe.createRunner();
inputStore.metaUrl = url;
inputStore.metaLogin = teamdataarray[0].email;
inputStore.metaPassword = teamdataarray[0].password;
inputStore.moderator = profilearray[i].profil;
inputStore.message = profilearray[i].template;
inputStore.channelid = profilearray[i].channelid;
})
.then(() => {
return runner
.src([__basedir + "/tests/temp.js"])
.browsers(browserarray)
.screenshots("", false)
.run()
.then((failedCount) => {
testcafe.close();
if (failedCount > 0) {
console.log(profilearray[i].profil);
console.log("No Success. Fails: " + failedCount);
//clearInterval(intervalId);
//reject("Error");
} else {
console.log(profilearray[i].profil);
console.log("All success");
//clearInterval(intervalId);
//resolve("Fertig");
}
});
})
.catch((error) => {
testcafe.close();
console.log(profilearray[i].profil);
console.log("Testcafe Error" + error);
//clearInterval(intervalId);
//reject("Error");
});
//######################################################################
i++;
console.log("Counter " + i);
if (i === profilearray.length) {
clearInterval(intervalId);
resolve("Fertig");
}
return exampleFunction;
})(),
3000
); //15 * 60 * 1000 max time to wait (user input)
});
}
The way I have done works bad because in the first action it will not start the testcafe. But in all other actions it will do.
Anybody knows a better way to do this?
Scope:
Give a array of data and for each array start testcafe with a given waiting time. 3 seconds up to 15 minutes. Because in some cases 15 Minutes is a long time I want to start the first one without any waiting time.
Iam open for any suggestion
For modern JavaScript await and async should be used instead of then and catch.
This will make many things easier, and the code becomes more readable. You e.g. can use a regular for loop to iterate over an array while executing asynchronous tasks within it. And use try-catch blocks in the same way as you would in synchronous code.
// a helperfunction that creates a Promise that resolves after
// x milliseconds
function wait(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
async function rollDice(profilearray, browserarray, url) {
for (let i = 0; i < profilearray.length; i++) {
// depending how you want to handle the wait you would create
// the "wait"-Promise here
// let timer = wait(3000)
let testcafe = await createTestCafe("localhost", 1337, 1338, void 0, true);
try {
let runner = testcafe.createRunner();
inputStore.metaUrl = url;
inputStore.metaLogin = teamdataarray[0].email;
inputStore.metaPassword = teamdataarray[0].password;
inputStore.moderator = profilearray[i].profil;
inputStore.message = profilearray[i].template;
inputStore.channelid = profilearray[i].channelid;
let failedCount = await runner.src([__basedir + "/tests/temp.js"])
.browsers(browserarray)
.screenshots("", false)
.run()
if (failedCount > 0) {
// ...
} else {
// ...
}
} catch (err) {
console.log(profilearray[i].profil);
console.log("Testcafe Error" + error);
} finally {
testcafe.close();
}
// Here you would wait for the "wait"-Promise to resolve:
// await timer;
// This would have similar behavior to an interval.
// Or you wait here for a certain amount of time.
// The difference is whether you want that the time the
// runner requires to run counts to the waiting time or not.
await wait(3000)
}
return "Fertig"
}
Declare function before setInterval, run setInterval(exampleFunction, time) and then run the function as usual (exampleFunction()). May not be ideal to the millisecond, but if you don't need to be perfectly precise, should work fine.
Ask if you need further assistance
EDIT: now looking twice at it, why are you calling the function you pass as parameter to setInterval inside the setInterval?

Animation on a button during an ajax call. But when it takes but little?

I have a button on an html page. I click on it and an ajax call gets sent. The text on a button gets replaced with animated dots while a call is in progress.
The problem is that sometimes a call takes several seconds, and sometimes 0.5 or 1 second.
When it takes 0.5 or 1 second, UX becomes bad because the text on a button gets replaced with animated dots and then it returns. A user sees 2 blinks happenning too fast.
How can I improve this?
Something like this as your submit event handler. It'll finish animation immediately if a given amount of time has passed. If it hasn't, it'll wait that amount of time before finishing up.
let button_pressed = (e)=>{
let button = document.getElementById('button_id');
button.ajaxStarted = new Date().valueOf();
animate_waiting(); // start your dot animation effect
var XHR;
XHR = new XMLHttpRequest();
XHR.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200){ // done
let diff = new Date().valueOf() - document.getElementById('button_id').ajaxStarted;
if(diff < (1000 * 3)){ // three seconds have not yet passed
setTimeout(()=>{
call_finished(); // now finish animating and post content
}, (1000 * 3) - diff);
}else{ // three seconds have passed
call_finished(); // now finish animating and post content
}
}
}
XHR.open("GET", "/your_ajax_call?foo=bar", true);
XHR.send();
}
You have to join your request call with some timeout function and wait till both complete. The implementation on pure JS could look as follows:
Wrap your xhr request into a Promise
var makeRequest = function (url, method) {
// Create the XHR request
var request = new XMLHttpRequest();
// Return it as a Promise
return new Promise(function (resolve, reject) {
// Setup our listener to process compeleted requests
request.onreadystatechange = function () {
// Only run if the request is complete
if (request.readyState !== 4) return;
// Process the response
if (request.status >= 200 && request.status < 300) {
// If successful
resolve(request);
} else {
// If failed
reject({
status: request.status,
statusText: request.statusText
});
}
};
// Setup our HTTP request
request.open(method || 'GET', url, true);
// Send the request
request.send();
});
};
Create some waiting function
var justWait = function(interval) {
return new Promise(resolve => {
setTimeout(resolve, interval);
})
}
Use them together
// apply animation here
Promise.all([
makeRequest('https://some/url/here', 'GET'),
justWait(1000)
]).then(res => {
// remove animation here
})
.catch(err => {
// remove animation here also in case of error
});
This will guaranty that your animation would be at least as long as justWait interval but if the xhr request takes longer - it will wait untill it's finished.
If you are using rxjs library you can use zip instead of Promise.all however the general idea remains the same.

how to restrict from opening multiple alert back to back?

I have written a angular error Interceptor where i am checking for 401 unauthorized or session expired. it works perfect but the problem is i have used multiple GET apis which on session expire throwing multiple 401. i am displaying js alert and re-directing to login page. as i am getting multiple 401 ,the alert box also is showing multiple times.
ex: if i got 3 times 401. the alert box is showing 3 times back to each.
i used flag to check if one alert box is open make it true and the make the rest false.. but it is not working.
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
if (req.url.indexOf('/reset_password') > 1 ||
req.url.indexOf('/login') > 1) {
return next.handle(req);
}
return next.handle(req).pipe(catchError(err => {
if (err.status === 401) {
alert("Sesson expired, Please Login again!");
$('.modal').modal('hide')
this.AuthService.logout()
}
const error = err.error.message || err.statusText;
return throwError(error);
}))
}
i want to display only one alert box not multiple. on click of OK i want to close it.
You can use either debounce or throttle based on the requirements, here is an example implementation for debounce.
As you can see it only triggers once, and only after provided time in miliseconds since last call of the function.
function debounce(func, wait) {
var timeout;
return function () {
var context = this, args = arguments;
var later = function () {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
const myAlert = debounce((msg) => alert(msg), 1000)
myAlert("alert1");
myAlert("alert2");
You could perhaps change it a little if you want to keep all the messages instead of just the last one. But take into account that i changed implementation of debounce so it isn't exactly a debounce anymore.
var messages = "";
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
messages += arguments[0];
var later = function () {
timeout = null;
func.apply(context);
messages = "";
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
const myAlert = debounce(() => alert(messages), 1000)
myAlert("alert1");
myAlert("alert2");
setTimeout(() => {
// here i wait for another 2000 miliseconds so the function isnt called back to back
myAlert("alert3");
myAlert("alert4");
}, 2000);

JS: check if setTimeout has finished or been canceled

I am creating a script that will execute a task after a certain amount of time has passed. I want the user to be able to cancel execution of these tasks. I can do this by saving the timeout to a list which I then use to cancel all timeouts. However if the timeout completes normally it is still stored in that list.
How do I check if a timeout has already been completed or canceled before attempting to clear it?
var timeouts = [];
$(document).on('click', '.doTask', function() {
var timeout = setTimeout(() => {
doTaskAfter();
}, 10000);
timeouts.push(timeout);
});
$(document).on('click', '.cancelTasks', function() {
var i = timeouts.length;
while (i--) {
if (timeouts[i].finished || timeouts[i].timeoutCleared) { // How to I check this?
clearTimeout(timeouts[i]);
}
timeouts.splice(i, 1);
}
});
I think you can do it using promises.
Every time that you create a timer, put it inside of a promise, and push that promise into the array. The promise will resolve once the timer has ran.
Literally write your timer like..
setTimeout(() => {
resolve("True");
}, 10000);
From there, what you can do is this. When you walk through your code, use promise.race, along with a second promise that returns false.
let race = [timeouts[i],Promise.resolve("False") ]
Use Promise.race to find if the promise has resolved. This works because if they have both resolved, Promise.race will return the first Promise, which will return "True". If your timer is still out, then it will return false.
Promise.race(race).then((res, rej) => {
if(res === true) {
clearTimeout(timeouts[i]);
}
})
I don't know if I understood correctly, but why would you remove already finished timeouts?
The timeout can remove itself from the array once it completes:
timeouts.splice(timeouts.indexOf(timeout), 1);
Otherwise you can use clearTimeout and splice to kill all registered/running timeouts from your timeouts list.
var timeouts = [];
$(document).on('click', '.doTask', function() {
var timeout = setTimeout(() => {
doTaskAfter();
timeouts.splice(timeouts.indexOf(timeout), 1); // <=== HERE
}, 10000);
timeouts.push(timeout);
});
$(document).on('click', '.cancelTasks', function() {
var i = timeouts.length;
while (i--) {
clearTimeout(timeouts[i]);
timeouts.splice(i, 1);
}
});
One thing that may or may not matter is that calling clearTimeout on an already cleared or completed timeout doesn't do anything, so you might not need to keep track of it.
Otherwise, you can just keep track of the state, something like:
var timeouts = [];
$(document).on('click', '.doTask', function() {
var timeout = { state: 'waiting' };
timeout.id = setTimeout(() => {
timeout.state = 'finished';
doTaskAfter();
}, 10000);
timeouts.push(timeout);
});
$(document).on('click', '.cancelTasks', function() {
var i = timeouts.length;
while (i--) {
if (timeouts[i].state !== 'finished' && timeouts[i].state !== 'canceled') {
// i.e. timeouts[i].state is 'waiting'
clearTimeout(timeouts[i].id);
timeouts[i].state = 'canceled';
}
timeouts.splice(i, 1);
}
});
Just put the function that contains the code you need to execute inside the set timeout like this👇:
setTimeout(() => { youfunction()}, 2000)
yourfunction(){
put your code here
}
yourfunction will execute after 2 second.

How to call API every 2 seconds until desired result or timeout in async function

I need to call an api to get a status every 2 seconds if the response is running and first return when response is either complete or failed, or until 30 seconds have passed and the function times out.
This is what I have now which works, but I am sure it can be done much more efficient, but I simply can't figure it out at this point:
const getStatus = async (processId) => {
try {
const response = await fetch(`example.com/api/getStatus/${processId}`);
const status = await response.json();
return await status;
} catch(err) {
// handle error
}
}
Inside another async function using getStatus():
randomFunction = async () => {
let status = null;
let tries = 0;
let stop = false;
while (tries <= 15 && !stop) {
try {
status = await getStatus('some-process-id');
if (status === 'complete') {
stop = true;
// do something outside of loop
}
if (status === 'failed') {
stop = true;
throw Error(status);
}
if (tries === 15) {
stop = true;
throw Error('Request timed out');
}
} catch (err) {
// handle error
}
const delay = time => new Promise(resolve => setTimeout(() => resolve(), time));
if (tries < 15) {
await delay(2000);
}
tries++;
}
}
I would prefer to handle the looping inside getStatus() and in a more readable format, but is it possible?
EDIT:
I tried a solution that looks better and seems to work as I expect, see it here:
https://gist.github.com/AntonBramsen/6cec0faade032dfa3c175b7d291e07bd
Let me know if parts of the solution contains any solutions that are bad practice.
Your question is for javascript. Unfortunately I don't drink coffee, I can only give you the code in C#. But I guess you get the gist and can figure out how to translate this into java
Let's do this as a generic function:
You have a function that is called every TimeSpan, and you want to stop calling this function whenever the function returns true, you want to cancel, whenever some maximum time has passed.
For this maximum time I use a CancellationToken, this allows you to cancel processing for more reasons than timeout. For instance, because the operator wants to close the program.
TapiResult CallApi<TapiResult> <Func<TapiResult> apiCall,
Func<TapiResult, bool> stopCriterion,
CancellationToken cancellationToken)
{
TapiResult apiResult = apiCall;
while (!stopCriterion(apiResult))
{
cancellationToken.ThrowIfCancellationRequested();
Task.Delay(delayTime, cancellationToken).Wait;
apiResult = apiCall;
}
return apiResult;
}
ApiCall is the Api function to call. The return value is a TApiResult. In your case the status is your TApiResult
StopCriterion is a function with input ApiResult and output a boolean that is true when the function must stop. In your case this is when status equals complete or failed
CancellationToken is the Token you can get from a CancellationTokenSource. Whenever you want the procedure to stop processing, just tell the CancellationTokenSource, and the function will stop with a CancellationException
Suppose this is your Api:
Status MyApiCall(int x, string y) {...}
Then the usage is:
Timespan maxProcessTime = TimeSpan.FromSeconds(45);
var cancellationTokenSource = new CancellationTokenSource();
// tell the cancellationTokenSource to stop processing afer maxProcessTime:
cancellationTokenSource.CancelAfter(maxProcessTime);
// Start processing
Status resultAfterProcessing = CallApi<Status>(
() => MyApiCall (3, "Hello World!"), // The Api function to call repeatedly
// it returns a Status
(status) => status == complete // stop criterion: becomes true
|| status == failed, // when status complete or failed
cancellationTokenSource.Token); // get a token from the token source
TODO: add try / catch for CancellationException, and process what should be done if the task cancels
The function will stop as soon as the stopCriterion becomes true, or when the CancellationTokenSource cancels. This will automatically be done after maxTimeOut. However, if you want to stop earlier, for instance because you want to stop the program:
cancellationTokenSource.Cancel();

Categories