Consider the code below. This code sets an interval of 5 seconds before making another request to load the new values into part of a document.
The problem is that when a request takes a long time to resolve (>5 seconds) the requests will pile up. How can we make sure the new request will only be made when the request has finished?
In my internet search trying to answer this question, I have not come across this specific situation including setInterval. I came across a couple of suggestions using async or promises, but I have yet to discover how those could be implemented in this situation. Please forgive my inexperience in the topic.
window.onload = () => {
setInterval(refresh, 5000);
}
function refresh() {
let myVariable = document.getElementById("myId").value;
// reload part of the page for the new values
$("#partial-id").load("partial.html", {
myparameter: myVariable ,
});
}
Call the function again in the .load() callback function.
function refresh() {
let myVariable = $("#myId").val();
$("#partial-id").load("partial.html", {
myparameter: myvariable
}, function() {
setTimeout(refresh, 5000);
});
}
This will repeat the function 5 seconds after the previous one completed.
If the task performed gets wrapped into a promise, one can await it in an infinite loop:
function refresh() {
let myVariable = document.getElementById("myId").value;
return new Promise(res => {
// reload part of the page for the new values
$("#partial-id").load("partial.html", {
myparameter: myVariable ,
}, res);
});
}
(async function () {
while(true) {
await refresh();
await new Promise(res => setTimeout(res, 5000));
}
})();
Related
I'm doing a polling method to my API every 5 seconds, to get real-time data. The code below works but after 1 hour of running, the page crash(Aww Snap, with the dinosaur image and error: out of memory). The data I'm collecting is quite large, and I'm expecting that javascript will offload the memory(garbage collection) every time the function is being called again. I can see in the Chrome Task Manager, the memory footprint is growing over time. Is there a way to clear the memory or offload the memory from growing over time?
data(){
return{
newdata:[],
};
},
methods: {
loadData:async function () {
try {
let response = await axios.get('/monitoring_data');
if (response.status != 200) {
await new Promise(resolve => setTimeout(resolve, 1000));
await this.loadData();
}else {
// Get the data
this.newdata= response.data.Ppahvc;
// Call loadData() again to get the next data
await new Promise(resolve => setTimeout(resolve, 5000));
await this.loadData();
}
} catch (e) {
await this.loadData();
}
},
},
mounted:function(){
this.loadData();
},
You can use setinterval instead of calling the loadData function recursivley
You are calling the loadData function recursivley (function calls itself) which is likely the cause of your memory issue because the browser has to remember these nested calls to eventually return again.
Try using an iterative approach instead:
setInterval(function {
let response = await axios.get('/monitoring_data');
this.newdata = response.data.Ppahvc;
}, 5000);
I have a function that solves a puzzle in a browser. It might take a very long time to finish and I want to stop its execution if the runtime exceeds 30 seconds. Something like this:
function solve(puzzle) {
// A loop to solve a puzzle that might take minutes to run
while (notSolve){
keepFindingSolution(); // This is not a loop
}
return solution;
}
let solution = solve(veryHardPuzzle);
if (solution && solution.solved) {
// Do something
}
console.log("Other executions");
So, I don't want to block the UI thread when solving the function so that users can still interact with the website. I also want to get the solution from the solve(veryHardPuzzle) once it's done, or break the execution of the function if the time is out.
I tried some different approaches below, but none of them work for me:
Wrap the solve() function in setTimeout()
setTimeout(function () {
let solution = solve(veryHardPuzzle);
if (solution && solution.solved) {
// Do something
}
}, 30000);
console.log("Other executions");
This approach (from https://stackoverflow.com/a/26616321/6308776) doesn't block the UI thread, and the other executions are executed happily. However, it just basically waits 30 seconds before executing the inner solve(veryHardPuzzle) function (please correct me if I'm wrong). If the solve(veryHardPuzzle) function runs longer than 30 seconds then it would block the thread.
clearTimeout() once the solve() is done
let timerId = setTimeout(function () {
let solution = solve(veryHardPuzzle);
clearTimeout(timerId);
if (solution && solution.solved) {
// Do something
}
}, 30000);
console.log("Other executions");
I thought that this would stop the timeout() after the solution is found, but it technically waits 30 seconds before the solver(veryHardPuzzle) is executed.
After doing some research, I realized setTimeout() might not the thing I want. Do you have any ideas or techniques on how to solve this?
The easiest way to handle asynchronous (like) behavior is with a Promise.
In this case you could do something like this:
function doLongThing() {
let success = false;
return new Promise(function(resolve, reject) {
//set timeout so if success is false after 30 seconds, reject promise.
setTimeout(function () {
if(!success) reject();
}, 30000)
//another set timeout to prevent blocking UX thread
setTimeout(function () {
//do long running thing
let solution = solve(veryHardPuzzle);
//thing finished
success = true;
//pass result into resolve method so consumer can use it
resolve(solution);
});
})
}
then you could do this:
doLongThing().then(function(solution) {
//do something with solution
}).catch(function() {
//timed out
});
console.log("Other executions");
A few comments have hinted at this, but the answer is not in how you call solve, but rather in how solve iterates over each “try” (I’m assuming this is how your solver works). If after each try your solver will break out of the event loop (the essence of async JS), then the main thread will have opportunities to do the other work it needs to do like handle mouse clicks or paint the UI. There are a few ways to do this, but maybe Promises is the easiest to visualize and has the best browser support (though some polyfills may be necessary for some IE versions).
var timeIsUp = false
function trySolution (puzzle, solution) {
return new Promise(function (resolve, reject) {
if (!solution) reject('no solution found')
if (timeIsUp) reject('time up!')
if (puzzle + solution !== 'wrong') { // idk lol
resolve({solution: solution, success: true})
} else {
resolve({solution: solution, success: false})
}
}
}
// solve is now recursive with each iteration async
function solve (puzzle, solutions) {
trySolution(puzzle, solutions[0])
.then(function (result) {
if (result.success) {
solve(puzzle, solutions.slice(1))
}
})
.catch(function (err) {
console.error(err)
})
}
// do the deed
solve(puzzle, solutions) // puzzle is undefined error lol
setTimeout(function () {
timeIsUp = true
}, 30000)
I have a function createSession for getting users with returned session object. createSession either returns users or throws string exception. My code is as follow, if session is successfull (which means I have users) continue code.
However I do not know when users are available because other resources also asks for it. So, I want a modify my code that it will try getting users in a timeout, lest say try it every 1 second until timeout 30 seconds reached.
async function callTest() {
const capabilities = {
type: {
chrome: 2
}
}
var session = await working.createSession(capabilities)
if(!session) return
callTest()
// remaining code goes here
}
I though to use setTimout in a promise but the problem is I can not use await inside a normal function. Can someone help me fixing my code ?
var checkAuth = function(capabilities) {
return new Promise(function(resolve) {
var id = setInterval(function() {
var session = await working.createSession(capabilities)
if (session) {
clearInterval(id);
resolve(id);
}
});
}, 10);
});
}
You can try to use it like this and it should work well
var checkAuth = function(capabilities) {
return new Promise(function(resolve) {
setInterval(async function() {
var session = await working.createSession(capabilities)
if (session) {
clearInterval(id);
resolve(id);
}
}, 10);
});
}
You should add async keyword to function definition in setInterval, because await can only work in async functions.
Update: also move ,10 parameter from Promise definition to setInterval one
I am running a HTTP Request to a file and depending on the response whether it be "200" or another response a success or error function is ran. This request takes place every second.
The problem I am facing is when I get lots of error responses they all run together and the last one doesn't stop e.g. End the interval to start a new one.
The red light begins to flash way too fast. Can anyone help me out. My code is below and I have been playing with it for a few hours now but can't seem to get to the bottom of it.
var requestResponses = {
greenLight: $('.cp_trafficLight_Light--greenDimmed'),
redLight: $('.cp_trafficLight_Light--redDimmed'),
greenBright: 'cp_trafficLight_Light--greenBright',
redBright: 'cp_trafficLight_Light--redBright',
init: function (url) {
setInterval(function () {
requestResponses.getResponse(url);
}, 1000);
},
successResponse: function () {
var redBright = requestResponses.redBright,
greenBright = requestResponses.greenBright;
requestResponses.errorCode = false;
requestResponses.redLight.removeClass(redBright);
requestResponses.greenLight.addClass(greenBright);
},
errorResponse: function () {
requestResponses.runOnInterval();
},
runOnInterval: function () {
// clearInterval(runInterval);
var redBright = requestResponses.redBright,
greenBright = requestResponses.greenBright,
redLight = requestResponses.redLight;
requestResponses.greenLight.removeClass(greenBright);
var runInterval = setInterval(function () {
if (requestResponses.errorCode === true) {
redLight.toggleClass(redBright);
}
}, 400);
},
getResponse: function (serverURL) {
$.ajax(serverURL, {
success: function () {
requestResponses.errorCode = false;
requestResponses.successResponse();
},
error: function () {
requestResponses.errorCode = true;
requestResponses.errorResponse();
},
});
},
errorCode: false
}
requestResponses.init('/status');
Appreciate the help.
Javascript is an event driven language. Do not loop inifinitely to check things periodically. There are places to do so but most of the time either calling a delay function (setTimeout) repeatedly when needed or using a callback would be better method.
Using setInterval with request, think what happens if requests start taking longer than your interval.
In your case, you have two loops created with setInterval. First one is the request which will run every 1 sec. Instead of using setInterval, you can modify your code to run setTimeout only after a request finishes and do other tasks just before re-running the next request :
function runRequest(...) {
$.ajax(serverURL, {
...
complete: function () {
setTimeout(runRequest, 1000);
}
...
});
}
function lightsOnOff() {
var redBright = requestResponses.redBright,
greenBright = requestResponses.greenBright,
redLight = requestResponses.redLight;
requestResponses.greenLight.removeClass(greenBright);
if (requestResponses.errorCode === true) {
redLight.toggleClass(redBright);
}
}
setInterval(lightsOnOff, 400);
The setInterval() method repeats itself over and over, not just one time. Your error response handler is then invoking the routine that creates another setInterval(), and so on. Until you have so many processes running that you get the flashing red light issue.
The solution is to only invoke the logic where the setInterval() call is made once. Or, even better, use setTimeout() to call the routine. It is run one-time and likely better for your use.
For a simple web app that needs to refresh parts of data presented to the user in set intervals, are there any downsides to just using setInterval() to get a JSON from an endpoint instead of using a proper polling framework?
For the sake of an example, let's say I'm refreshing the status of a processing job every 5 seconds.
From my comment:
I would use setTimeout [docs] and always call it when the previous response was received. This way you avoid possible congestion or function stacking or whatever you want to call it, in case a request/response takes longer than your interval.
So something like this:
function refresh() {
// make Ajax call here, inside the callback call:
setTimeout(refresh, 5000);
// ...
}
// initial call, or just call refresh directly
setTimeout(refresh, 5000);
A simple non-blocking poll function can be implemented in recent browsers using Promises:
var sleep = duration => new Promise(resolve => setTimeout(resolve, duration))
var poll = (promiseFn, duration) => promiseFn().then(
sleep(duration).then(() => poll(promiseFn, duration)))
// Greet the World every second
poll(() => new Promise(() => console.log('Hello World!')), 1000)
You can do just like this:
var i = 0, loop_length = 50, loop_speed = 100;
function loop(){
i+= 1;
/* Here is your code. Balabala...*/
if (i===loop_length) clearInterval(handler);
}
var handler = setInterval(loop, loop_speed);
Just modify #bschlueter's answer, and yes, you can cancel this poll function by calling cancelCallback()
let cancelCallback = () => {};
var sleep = (period) => {
return new Promise((resolve) => {
cancelCallback = () => {
console.log("Canceling...");
// send cancel message...
return resolve('Canceled');
}
setTimeout(() => {
resolve("tick");
}, period)
})
}
var poll = (promiseFn, period, timeout) => promiseFn().then(() => {
let asleep = async(period) => {
let respond = await sleep(period);
// if you need to do something as soon as sleep finished
console.log("sleep just finished, do something...");
return respond;
}
// just check if cancelCallback is empty function,
// if yes, set a time out to run cancelCallback()
if (cancelCallback.toString() === "() => {}") {
console.log("set timout to run cancelCallback()")
setTimeout(() => {
cancelCallback()
}, timeout);
}
asleep(period).then((respond) => {
// check if sleep canceled, if not, continue to poll
if (respond !== 'Canceled') {
poll(promiseFn, period);
} else {
console.log(respond);
}
})
// do something1...
console.log("do something1...");
})
poll(() => new Promise((resolve) => {
console.log('Hello World!');
resolve(); //you need resolve to jump into .then()
}), 3000, 10000);
// do something2...
console.log("do something2....")
I know this is an old question but I stumbled over it, and in the StackOverflow way of doing things I thought I might improve it. You might want to consider a solution similar to what's described here which is known as long polling. OR another solution is WebSockets (one of the better implementations of websockets with the primary objective of working on all browsers) socket.io.
The first solution is basically summarized as you send a single AJAX request and wait for a response before sending an additional one, then once the response has been delivered, queue up the next query.
Meanwhile, on the backend you don't return a response until the status changes. So, in your scenario, you would utilize a while loop that would continue until the status changed, then return the changed status to the page. I really like this solution. As the answer linked above indicates, this is what facebook does (or at least has done in the past).
socket.io is basically the jQuery of Websockets, so that whichever browser your users are in you can establish a socket connection that can push data to the page (without polling at all). This is closer to a Blackberry's instant notifications, which - if you're going for instant, it's the best solution.