(I've looked at all similar questions/answers but none of them solve my problem.)
The code:
var timeoutHandle;
function showLoader(show) {
if (show) {
$('.loader').html('Loading...');
$('.loader').show();
timeoutHandle = setTimeout(function () {
if ($('.loader').is(':visible')) {
$('.loader').html('Still loading...');
}
}, 15000);
}
else {
$('.loader').hide();
clearTimeout(timeoutHandle);
}
}
The AJAX function simply calls showLoader(true) before calling the server, and then showLoader(false) after a result. I still sometimes see the text change from "Loading..." to "Still loading..." long before 15 seconds, so it's as if a timer thread is still running. Is there something wrong with the code above? Or could the problem be with other code..
edit: I must add that showLoader(true) can be called again (and again) before a response from the server
You should add a check to see if there is already a timeoutHandle before creating a new one.
try this:
if(timeoutHandle){
clearTimeout(timeoutHandle);
timeoutHandle = null;
}
timeoutHandle = setTimeout(function () {
if ($('.loader').is(':visible')) {
$('.loader').html('Still loading...');
}
}, 15000);
and then in the else case set timeoutHandle to null after you clear it like so:
clearTimeout(timeoutHandle);
timeoutHandle = null;
This will eliminate the chance of you creating concurrent timeouts if showLoader(true) function is called more than once.
What might be happening is that you're placing multiple calls to showLoader since this is a global function you can access it from anywhere, you typically don't want that.
I would consider changing it to a monad implementation:
function create_loader(elem) {
var handle = null;
function show() {
elem.html('Loading...');
elem.show();
if (handle !== null) {
clearTimeout(handle); // clear the previous one
}
handle = setTimeout(function () {
elem.html('Still loading...');
}, 15000);
}
return {
show: show,
clear: function () {
elem.hide();
clearTimeout(handle);
handle = null;
}
};
}
Usage:
var loader = create_loader($(".loader"));
loader.clear();
loader.show();
loader.show(); // each new call to show will reset the 15s timer
loader.show();
loader.show();
loader.clear();
// and you can make another one that operates independently of other one
var another_loader = create_loader($(".anotherLoader"));
Now you have a loader object that knows about it's own state.
In your post you mention that showloader can be called multiple times before the first return. This is your problem. You are overwriting an already existing timeoutHandle with a new one without destroying the already existing handle. You should check if the timeoutHandle is set or not set before you create a new one.
You don't call clearTimeout(timeoutHandle) then starting new request, if timeoutHandle exist
Related
I am using an already defined function and now want to add a pollServer function to it so that this functions runs over and over. I keep getting errors when I try to wrap the existing function in another. Is there a better way to do this?
function callD(id) {
jQuery('document').ready(function pollServer(){
window.setTimeout(function () {
var ab = document.getElementById('a')
console.log(ab);
var bod = document.getElementById(+id)
if (ab == null) {
bod.style.background='green'
} else {
bod.style.background='blue'
}
}, 1200);
})
}
callD();
pollServer();
pollServer isn't defined where you're calling it. Also id isn't being passed to callD, and you also have a +id which doesn't make sense in a document.getElementByid, since if there's any non-number in the ID, that would be NaN. You're also not polling a server, you're setting a timeout once and doing some work that doesn't involve a server. You would want setInterval for regular polling, or to call the function again on some condition like a failure.
$(document).ready(function () {
var intervalId;
function callD(id) {
function pollServer() {
intervalId = window.setInterval(function () {
var ab = document.getElementById('a')
console.log(ab);
var bod = document.getElementById(id)
if (ab == null) {
bod.style.background='green'
} else {
bod.style.background='blue'
}
}, 1200);
}
pollServer();
}
callD('some-id');
// on some condtion eventually:
clearInterval(intervalId);
})
Yeah, jQuery can make things pretty gnarly with all the nested callbacks. To make the code cleaner and easier to understand, I like to split my functions up and define them all at the top-most level of the script, then compose them together like so:
/**
* this function will check for the existing elements
* and update styles
*/
function setBodyStyle(id) {
var ab = document.getElementById('a');
console.log(ab);
var bod = document.getElementById(+id);
if (ab == null) {
bod.style.background='green';
} else {
bod.style.background='blue';
}
}
/**
* this function will create a timeout or interval
* which will in turn run setBodyStyle()
*/
function pollServer() {
// I think you want setInterval here if you're polling?
// setInterval will run _every_ 1200ms,
// setTimeout only runs once after 1200ms
window.setInterval(function() {
// not sure where you're getting id from,
// but you'll want to pass it here
setBodyStyle();
}, 1200);
}
// when the document is ready, run pollServer()
jQuery(document).ready(pollServer);
Having small functions that do one thing is just best-practice for the reasons I mentioned above. This will help your script be more understandable, which will help you find bugs.
For example, two things I don't understand about your code above:
where does the id variable come from? I don't see you passing it to your function from anywhere
how does your script poll the server? I don't see the code for that anywhere either.
Seemed you mean run the function pollServer every 1.2 sec. If so, you'd need to do two things
Use setInterval rather than setTimeout
Delete the last line for the pollServer function, because it is not accessible from outside the ready function block.
I've written some code that when you click a button it adds an instance of a function to an array,
var objects = [];
$(document).on("click", ".addButton", function(){
objects.push(new newObject(1));
});
function newObject(amount){
setInterval(function(){
addValue(amount);
}, 1000);
}
So then every second each new object created keeps running the addValue function every second adding the amount.
The problem is when I try and destroy that function with objects.pop() it deletes the object but the setInterval doesn't stop running.
How do I make it destroy everything in that function and stop it from running?
There is nothing quite like that in JS for setInterval. I would suggesting declaring a method to handle clean up.
// "Class" declaration
function newObject(amount) {
var id = setInterval(function() {
addValue(amount);
}, 1000);
this.kill = function() {
clearInterval(id);
}
}
// "Public" api for the data structure
var objects = [];
function addNewObject() {
objects.push(new newObject(1));
}
function destroyLastObject() {
objects.pop().kill();
}
// Event bindings
$(document).on("click", ".addButton", addNewObject);
$(document).on("click", ".removeButton", destroyLastObject);
Completely untested, but along these lines should work.
EDIT
This, imo, is a great resource for learning about different patterns within javascript - long but well well worth the read: https://addyosmani.com/resources/essentialjsdesignpatterns/book/
You got to find something to check against to clear the interval. I am clearing based on array length. It only executes once.
// you got to find something to check against to clear the interval
var objects = [];
document.addEventListener("click", function(){
console.log('click');
objects.push(new newObject(1));
});
function newObject(amount){
var interval= setInterval(function(){
if(objects.length !==0){
clearInterval(interval);
}
}, 1000);
}
I have a simple js structure like this :
var Waiting = (function () {
function Waiting() {
this.timer;
}
Waiting.prototype.show = function () {
var self = this;
clearTimeout( self.timer );
self.timer = setTimeout( function(){ self.hideLogo(); },3000);
}
Waiting.prototype.hideLogo = function () {
console.log("ok i passed timeout");
};
return Waiting;
})();
As expected, I get the "ok i passed timeout" log on every browser the first time I execute the show function (which called the hideLogo one). The problem appears in IE9 when I called for the second time the show function. This time, the hideLogo function is never called (log never appears in IE console). I tried a lot of things without any success.
If anyone as an idea...
When you're using setTimeout, the function that is being called looses the context: in other words this doesn't post to the instance on which the method is called anymore. You're using self to cancel this issue out, but self is, itself, an iffy word (as in reserved keyword). Perhaps use that, and use an IIFE in the setTimeout call:
this.timer = setTimeout((function (that)
{
return function()
{
clearTimeout(that.timer);//perhaps clear timeout here?
that.hideLogo.apply(that,[]);//double dutch, the apply _shouldn't_ be required
};
}(this)), 3000);
At first glance, that's the only thing I can see that might be the issue with your code: the clearTimeout call shouldn't be an issue, but I like to call it at the end of the timeout itself, and the self ambiguity thing. Let me know if this changes anything for you!
I am not really sure how you'd call show the second time with the code provided, maybe you create a new Waiting()?
Here is what worked for IE8
var Waiting=(function () {
function Waiting() {
this.timer;
}
Waiting.prototype.show = function () {
var self = this;
console.log("will clear pref timeout");
clearTimeout( self.timer );
self.timer = setTimeout(
function(){
self.hideLogo();
},30);
}
Waiting.prototype.hideLogo = function () {
console.log("ok i passed timeout");
};
return new Waiting();
})();
// shows only one time
Waiting.show();
Waiting.show();
// next one will show because it lets the prefious one
// finish without clearing the pref timeout.
setTimeout(function(){
Waiting.show();
},1000);
Try:
setTimeout( function(){
clearTimeout( that.timer );
that.hideLogo();
},3000);
Worked for me on IE and Chrome. IE is very behind on everything.
I have a recursive type function in Javascript that runs like this:
function loadThumb(thumb) {
rotate=setTimeout(function() {
loadThumb(next);
}, delay);
}
Note: I've simplified the function to make it easier to read.
I have "a" tags called like this
Load thumb 3
However, they don't clearout the timer, the timer continues to cycle through the function irregardless of the clearTimeout() being called.
Any ideas why? I think it might have something to do with a scope problem or something like that.
Yeah, you need to make rotate a global variable. Simply declare it outside the function like so:
var rotate;
var delay = 1000;
function loadThumb(thumb) {
alert("loading thumb: " + thumb);
rotate = setTimeout(function() {
loadThumb(thumb + 1);
}, delay);
}
Also, you need to make sure you clear the timeout before you call loadThumb. Otherwise you'll clear the timer you just started.
Load thumb 3
fiddle: http://jsfiddle.net/63FUD/
it may be the issue of scope so make rotate as global variable and call clearTimeout(rotate);
refer clearTimeout() example
It may be a scoping issue if you are not declaring rotate externally.
Try this:
var rotate = 0;
function loadThumb(thumb) {
rotate=setTimeout(function() {
loadThumb(next);
}, delay);
}
Return false on the link
Since you are not using var rotate, it should not be a scoping issue since rotate would be in the window scope. Can you show the complete code?
It is considered poor coding to inline the script - you should attach the event handler onload of the page
Also you should not have the setTimeout inside a function that might be called for one image
Try this:
var rotate,next=1;
function loadThumb(thumb) {
if (thumb) ... use thumb
else ... use next
}
function slide() {
rotate=setInterval(function() {
loadThumb();
next++;
if (next>=images.length) next=0;
}, delay);
}
window.onload=function() {
var links = document.getElementsByTagName("a");
if (links[i].className==="thumbLink") {
links[i].onclick=function() {
var idx = this.id.replace("link","");
loadThumb(idx);
clearInterval(rotate);
return false;
}
}
document.getElementById("start").onclick=function() {
slide();
return false;
}
document.getElementById("stop").onclick=function() {
clearInterval(rotate);
return false;
}
slide();
}
assuming
Start
Stop
Show 1
Show 2
Show 3
If you have to manage multiple timeouts, you can use an object in the global scope and some custom methods to create and remove your timeouts. To access the methods you can either put the calls in the onclick handler of your links (like in the example), or use a library like jQuery to bind them.
<script type="text/javascript">
var timeouts = timeouts || {};
function createTimeout(name, milliseconds, callback) {
timeouts.name = setTimeout(callback, milliseconds);
}
function removeTimeout(name) {
if (typeof(timeouts.name) !== undefined) {
clearTimeout(timeouts.name);
timeouts.name = undefined;
}
}
createTimeout('foo', 5000, function() {
alert('timeout')
});
</script>
i have also posted an example on jsFiddle http://jsfiddle.net/AGpzs/
I'm not sure what exactly you are doing, because as far as I can see you didn't post all the code, but this looks better for me:
function loadThumb(thumb) {
return setTimeout(function() {
loadThumb(next);
}, delay);
}
and then:
Load thumb 3
If I have an element that responses on
$('#div').keydown(function(event) { ....
and if user presses a key like a crazy rabbit on heat thousand times within a really short period, browser responses on most of those calls.
Can I somehow prevent that by flushing keyboard buffer?
This is an easy method to deal with an excessive number of keydown calls.
var timeout = false; //I'd recommend defining this variable within a local scope
$('#div').keydown(function(event) {
if(timeout) return;
timeout = true;
setTimeout(function(){timeout=false}, 100);
//Change 100 to something more appropriate
//Rest of function
}
You may checkout the following blog post which illustrates how you could throttle down function calls and calm down the crazy rabbits.
function debounce(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}
$('input.username').keypress(debounce(function (event) {
// do the Ajax request
}, 250));