setInterval not allow simultaneous click event - javascript - javascript

I have polling question for you. I am polling information every 20 seconds using setInterval, and have click events that when fired, they pass objects to a method. The problem comes when I click on a button and the event fires at the same time the poll restarts. If that happens, the object that is passed is undefined, which makes sense because when we restart the poll, the information is refreshed. So the question is, how/can one "block" an event from firing when a setInterval is restarting?
Thanks

Going to show some dummy code to illustrate the idea of a queue while you refresh the data on the page:
$(function() {
var isRefreshing = true;
var queue = [];
setInterval(function() {
isRefreshing = true;
$.ajax({
/*
settings
*/
success: function() {
isRefreshing = false;
//process queue
var item;
while(item = queue.pop()) {//if order matters use shift
worker(item);
}
}
});
}, 20*1000);
var worker = function(/*params*/) {
//stuff
}
$("#my-element").click(function() {
var data = {};
if(isRefreshing) {
queue.push(data)
} else {
worker(data);
}
});
});

Related

Reliably clear intervals when switching page on a dynamic app

I load all content inside a div in the index. Some of these pages require starting intervals, and when switching page those intervals keep going, so I created a destroyjs() function which gets overridden by pages that need it, and is also called every time you switch pages.
The goServiceXXX functions are called onclick in the website's navbar.
var destroyjs = function(){};
function loading(elem)
{
if (typeof destroyjs == 'function')
destroyjs();
document.getElementById("mbody").innerHTML = '<div class="container4"><img src="dist/img/loading.gif" alt="Loading..."></div>';
}
function goServiceManager()
{
var element = "#mbody";
var link = "loadservicemanager.php";
loading(element);
$(element).load(link, function(){
reloadServerStatus();
statusInterval = setInterval(function()
{
reloadServerStatus();
}, 2000);
destroyjs = function(){
clearInterval(statusInterval);
clearInterval(modalInterval);
alert("destroyed manager, interval #"+statusInterval);
}
});
}
function goServiceMonitor()
{
var element = "#mbody";
var link = "loadservicemonitor.php";
loading(element);
$(element).load(link, function(){
reloadServerStatus();
statusInterval = setInterval(function()
{
reloadServerStatus();
}, 2000);
destroyjs = function(){
clearInterval(statusInterval);
alert("destroyed monitor, interval #"+statusInterval);
}
});
}
This works fine when used normally however if I spam click between the two pages, intervals start adding up and the 2 second query is now being called 10 times every two seconds. I added the alerts to debug but they slow the interface down enough for everything to work properly.
Is there a hole in my logic somewhere? I already thought of disabling all navbar buttons when one is clicked and enabling them at the end of .load; but I'd like to know why my current implementation is not working and if it can be fixed more easily.
Edit:: So I tried to make a fiddle with the problem and in the process realized that the problem happens when destroyjs() is called before .load() finishes. Moving the interval right before .load() fixes the problem but can create a scenario where if the content never loads (or doesn't load in two seconds) there are missing elements which the function inside the interval tries to fill. Disabling the navbar temporarily and wait for .load to finish is the easy way out after all but I'd still like more opinions on this or maybe ideas for a better implementation.
destroyjs isn't defined until the load() completes. If you switch tabs before the previous tab has loaded, you won't be able to call the correct destroyjs.
Therefore, you will want to cancel any outstanding load requests when switching tabs. To do this, you can use jQuery's ajax method. Just store a reference to the resulting XHR object and call abort() when loading a new tab. Aborting an outstanding ajax request will prevent it's success callback from firing.
Here's an example (DEMO FIDDLE). Notice that I've also changed the way that intervals are cleared:
//ajax request used when loading tabs
var tabRequest = null;
//Any intervals that will need to be cleared when switching tabs
var runningIntervals = [];
function loading(elem)
{
if (tabRequest) {
//Aborts the outstanding request so the success callback won't be fired.
tabRequest.abort();
}
runningIntervals.forEach(clearInterval);
document.getElementById("mbody").innerHTML = '<div>Loading...</div>';
}
function goServiceManager()
{
var element = "#mbody";
var link = "loadservicemanager.php";
loading(element);
tabRequest = $.ajax({
url: link,
success: function(data) {
$(element).html(data);
reloadServerStatus();
statusInterval = setInterval(reloadServerStatus, 2000);
modalInterval = setInterval(modalFunction, 2000);
runningIntervals = [statusInterval, modalInterval];
}
});
}
function goServiceMonitor()
{
var element = "#mbody";
var link = "loadservicemonitor.php";
loading(element);
tabRequest = $.ajax({
url: link,
success: function(data) {
$(element).html(data);
reloadServerStatus();
statusInterval = setInterval(reloadServerStatus, 2000);
runningIntervals = [statusInterval];
}
});
}

jQuery - Why document unloading stops my requests?

Some pages on my application are very, very slow to display, for normal reasons (business intelligence, that calls many external APIs, etc.). Sometimes, 3-5 minutes are needed.
I want to display a progress bar with text, notifying the user what's currently going on and why he's waiting.
Here's how I imagined the feature:
Every time the user displays a page, a new token is generated in the form. When he submits the form, while the browser waits for the response, a route can be called with the given token, that returns an JSON response giving the percentage of progress and the operation currently being done.
So, I wrote a little jQuery code that polls this route, and updates the progress bar as well.
var pollAjax = function () {
this.url = '';
this.interval = 5000;
this.callback = '';
this.running = true;
this.poll = function poll() {
var that = this;
if (that.running) {
$.get(that.url, function (data) {
that.callback(data);
setTimeout(function () {
that.poll();
}, that.interval);
});
}
};
this.setInterval = function setInterval(interval) {
this.interval = interval;
return this;
};
this.setCallback = function setCallback(callback) {
this.callback = callback;
return this;
};
this.setUrl = function setUrl(url) {
this.url = url;
return this;
};
this.stop = function stop() {
this.running = false;
return this;
}
};
$(document).ready(function () {
var progressBars = $('.progress .progress-bar');
progressBars.each(function () {
var progressPoll = new pollAjax();
var progressBar = $(this);
progressPoll.setUrl($(this).data('refreshurl'));
progressPoll.setInterval(2000);
progressPoll.setCallback(function (data) {
if (data.progress) {
$('#progressbar').show();
progressPoll.setInterval(200);
progressBar.attr('data-transitiongoal', data.progress.percentage).progressbar();
$('#progress_text').text(data.progress.text);
console.log(data.progress);
}
});
progressPoll.poll();
});
});
Sorry for the code quality, I'm better with PHP :)
This code works fine. When I fake some data in the poll url, the progressbar correctly updates every 200ms.
My problem is that this code stops working as soon as the user leaves the page (and that's bad because this is when I want the code to be triggered).
When he submits the form, the page is still displayed while the browser is waiting long minutes for the response, but the automatic poll immediately stops. As if the unload event prevents scripts to continue running, even if the page is still displayed.
Do you know how I can handle this ?
Thanks,
Ben

Waiting for webworker to finish

I have a webworker that does certain things in the background. After it is done , an event is triggered by it. I want pause the execution of my program till i get that event fired. In short i need to wait for that event to occur and after it is fired i need to resume the rest of my functionality. I been searching all over the internet and i couldn't find a suitable solution for this.
var worker = new Worker( workInBackground );
var workInBackground = function () {
//some stuff
$(document).trigger("done")
}
I need to pause for "done" to be fired and then continue my call-stack. I would really appreciate some help here
Just a timer, checking for a variable.
var checkWorkDone = false;
var workController = setInterval(function(){ CheckWork() }, 200);
function CheckWork() {
console.log("checkWorkDone = " + checkWorkDone );
if(checkWorkDone == true)
{
goOnToDoNextJob();
}
}
// when checkWorkDone == true, the work is done, so stop workController;
function goOnToDoNextJob() {
clearInterval(workController);
}
// this is just for checking, set var work done after 5 sec
var testWorker = setInterval(function(){ DoJob() }, 5000);
function DoJob()
{
checkWorkDone = true;
clearInterval(testWorker);
}

How to smooth a jQuery script that takes time to execute?

I have a jQuery script that searches in the DOM and shows the results in a list.
There is a simplified version of the script here: http://jsfiddle.net/FuJta/1/
There is usually a large number of results, so the script can take a while to execute. (In the example above, this is simulated with a function that delays the script). So if you type too fast in the searchbox, the script prevents you from typing, and it feels bad.
How could I change my script so that you can type freely, and the results show up when they are ready. I want something like the facebook search : if you type too fast, the results are just delayed, but you can still type.
Html
<p>Type in foo, bar or baz for searching. It works, but it is quite slow.</p><br/>
<input type="text" id="search"/>
<div id="container" style="display:none">
<div class="element">foo</div>
<div class="element">bar</div>
<div class="element">baz</div>
</div>
<div id="results">
</div>​
Javascript
$(function() {
function refreshResults() {
var search = $('#search').val();
var $filtered = $('#container .element').clone().filter(function() {
var info = $(this).text();
return info.toLowerCase().indexOf(search) >= 0;
});
$('#results').empty();
$filtered.each(function() {
$('#results').append($(this));
});
}
// simulating script delay
function pausecomp(millis) {
var date = new Date();
var curDate = null;
do {
curDate = new Date();
}
while (curDate - date < millis);
}
$('#search').keyup(function() {
pausecomp(700);
refreshResults();
});
});​
One solution could to refresh the results only when pressing enter. This way, the delay for searching the results feels ok. But I would prefer if I just delay the results and let the user freely type.
You should perform a search like this using asynchronous techniques. No doubt Facebook uses some sort of AJAX to request search results - which means getting the results from the server. This will help prevent the UI 'freeze' that you are currently experiencing.
Here is a very simple example of what you can try (it uses JQuery for the AJAX requests):
var searchInProgress = false;//used to work out if a search is in progress
var searchInQueue = false;//used to flag if the input data has changed
function getSearchResults(searchText){
if (searchInProgress ) {
searchInQueue = true;
return;
}
searchInProgress = true;
searchInQueue = false;
$.getJSON("URL",//URL to handle AJAX query
{ searchText: searchText},//URL parameters can go here
function (data) {
//handle your returned data here
searchInProgress = false;
if (searchInQueue){//text has changed, so search again
getSearchResults();
}
});
}
$('#search').keyup(function() {
getSearchResults($(this).val());
});
A few things to note: It is probably a good idea to handle failed AJAX requests to ensure you can reset the searchInProgress flag as needed. Also, you can add delays after the keyup as desired, but this all depends on how you want it too work.
From How to delay KeyPress function when user is typing, so it doesn't fire a request for each keystroke? :
var timeoutId = 0;
$('#search').keyup(function () {
clearTimeout(timeoutId); // doesn't matter if it's 0
timeoutId = setTimeout(refreshResults, 100);
});
It does what I want indeed.
Here's a solution that divides the search process into steps, returning flow to the browser during the process to allow the UI to respond.
$(function() {
function searchFunc($element,search) {
var info = $element.text();
return info.toLowerCase().indexOf(search) >= 0;
}
var searchProcessor = null;
function restartSearch() {
console.log('Restarting...');
// Clear previous
if (searchProcessor != null) {
clearInterval(searchProcessor);
}
$('#results').empty();
// Values for the processor
var search = $('#search').val();
var elements = $('#container .element').get();
console.log('l:',elements,elements.length);
// Start processing
searchProcessor = setInterval(function() {
if (elements.length == 0) {
// Finished searching all elements
clearInterval(searchProcessor);
searchProcessor = null;
console.log('Finished.');
} else {
console.log('Checking element...');
var $checkElement = $(elements.shift());
if (searchFunc($checkElement, search)) {
$('#results').append($checkElement.clone());
}
}
}, 10);
}
$('#search').keyup(function() {
restartSearch()
});
});
It only processes one element each time. That should probably be increased so it handles perhaps 10 or 100 each time around, but the important point is that the work is divided into chunks.
This solution should also be faster than the original because it doesn't clone() everything, only the elements that were matched.

How can I execute a setTimeout() only when the user is present (with the mouse movement) with jQuery

I have a function that updates a <div /> via AJAX:
function update() {
<!-- .ajax() -->
setTimeout(update(), 3000);}
}
What I need is that this is not executed when the user is not present on the website, so if there is no movement of the mouse (we will suppose that if move it is in the website) it will not update .mousemove(). By the way, there is any other thing that we can do to know is someone is active on the website?
How can this be done? Thank you in advance!
Edit: probably I explained bad. I need to know the way to only update when there is activity. Like Facebook does with his news feed, the front page. Thanks!
You could use a mousemove handler to track when the user last moved, and then have the process only happen if they last moved the mouse within X seconds. But of course, if the user is sitting there reading something, or if they're a keyboard-oriented kind of person, that will tend to miss that they are there... So you'd probably want to look at keydown as well.
Here's a mousemove example:
jQuery(function($) {
var count = 0, lastmove = new Date();
$(document).mousemove(function() {
++count;
lastmove = new Date();
$('#display').html("Moved " + count + " times");
});
});​
Then your update code could do this:
function update() {
if (new Date() - lastmove < 60000) { // 60 seconds
// Really do the update
}
else {
// Check back in a few seconds
setTimeout(update, 3000);
}
}
Off-topic, but you have an error in your update code. You have:
setTimeout(update(), 3000);
...which will call update immediately and then try to use its return value to schedule something to happen in three seconds. If you want the call to update to be scheduled to happen in three seconds, leave off the () after it:
setTimeout(update, 3000);
I think I might have ended up with something such as this. Avoids date arithmetic. Only cares whether there's been some activity since the last update().
window.activeFlag = false;
window.updateDelay = 3000;
$(document).bind('mousemove scroll keydown', function(){ activeFlag = true; });
function update() {
if(activeFlag) {
doWork();
activeFlag = false;
}
}
window.setTimeout(update, updateDelay);
edit: I've discovered a flaw in the code. The following is more appropriate:
window.activeFlag = false;
window.updateDelay = 3000;
$(document).bind('mousemove scroll keydown', function(){ activeFlag = true; });
function update() {
if(activeFlag) {
doWork();
activeFlag = false;
}
window.setTimeout(update, updateDelay);
}
update();
I think there is no easy way to determine if the user is present
I would use a combination of mousemove, scroll, keypress.
var bUpdate = false;
function update() {
if(bUpdate){
///perform your ajax request
}
}
$(document).mousemove(function(){
bUpdate = true;
setTimeout(function(){bUpdate=false;}, 3000);}
});

Categories

Resources