sample code:
var isExecutionOver = false,
myFunction = function() {
// does some asynchronous stuff and sets isExecutionOver to true
// when the asynchronous job is over.
};
myFunction();
// Kill myFunction if it takes more than 3 seconds
setTimeout(function() {
if(!isExecutionOver) {
// How do I kill myFunction?
}
}, 3*1000);
In the above snippet I am trying to kill (or in other words, stop execution of) myFunction if it is not able to do its job in given time (3 seconds in this case).
PS: Kindly assume that I do not have control over myFunction definition. The only place where I can work is inside setTimeout.
You have a very complicated question:
You can't actually kill a running function. You can sometimes set a flag that it can notice when it decides to check. It could stop doing what it is doing and return.
But remember that JavaScript is a single-threaded language. When the code in this function is running, there are few ways for any other code to set that flag.
Example 1: It seems like I remember something about how some browsers, for example, will let certain events be run from the event queue when an alert box pops up. Those events could set the flag.
Example 2: Another possibility is that the code in the function is not technically in the function but the function starts an interval timer that does the work. Any other code could set the flag and the code run at intervals could check the flag and stop repeating.
var abortWork = false;
function a() {
while (notDone) {
.. do some work ...
if (abortWork) {
return;
}
}
}
... whatever ...
abortWork = true;
You can stop a setTimeout() event that is waiting for the time to be over. (There is a similar way to stop a setInterval() repeated event.
var t = setTimeout(function() {
... code goes here ...
}, 20000);
... whatever ...
clearTimeout(t); // stop the timeout if it hasn't started running the function
You can stop an ajax request that is waiting for the response. (Note: An XHR object is also returned from pure javascript ajax call.) Note that the request has already gone to the server and this just tells it to ignore any future response to the request.
var xhr = $.ajax({ type: "POST", ... });
... whatever ...
xhr.abort(); //kill the request
Related
How can I stop/terminate a function which is already executed and still running? For example, I have this function:
function foo() {
setInterval(function() {
console.log("Foo Executed !");
}, 3000);
}
foo();
Now, this foo() function will run for unlimited time, when a specific event occurs, let's say a stop button has been clicked, then I want to stop this function.
In my case the function doesn't have setInterval() function. And what if the foo() function don't have the setInterval() method, but just many lines of code that get executed and I want to stop it from executing after a specific event.
Stopping a running function is a bit different than what you are actually showing for code, which is an asynchronous operation happening outside of the function that initiated it.
Running functions can only be terminated from within the function and that is done with either a return statement or by throwing an exception.
return can be called conditionally so that the function doesn't always exit at the same point. This is often the case with form validation functions - - if something is determined to be invalid, a return is encountered so that the form is not submitted. If everything is valid, the return is skipped and the form is submitted.
Here's a simple example with return:
function foo1(){
console.log("Foo started...");
if(prompt("Type 1 to terminate right now or anything else to continue...") == "1"){
return; // Function will terminate here if this is encountered
}
console.log("Foo ending..."); // This will only be run if something other than 1 was entered
}
foo1();
And, here's an example with throwing an error (not something that is usually done):
function foo(){
console.log("foo started...");
for(var i = 0; i < 5; i++){
if(i === 3) { throw "I HATE 3!"; }
console.log(i);
}
console.log("foo ended...");
}
foo();
But, with Timers and Intervals, you'll need to call clearInterval() and/or clearTimeout() to stop them. These are different because, while some function may initiate the timer or interval, the timer runs outside of the JavaScript runtime environment as a WebAPI. For these, we have to send a message to the WebAPI that we want the timer to stop counting.
You say:
Now, this foo() function will run for unlimited time, when a specific
event occurs, let's say a stop button has been clicked, then I want to
stop this function.
But foo isn't running for an unlimited time. It's run once and then terminates. Then approximately 3 seconds later, the timer calls for the anonymous function you passed to it to be run and then that function terminates and approximately 3 seconds later the anonymous function runs again and then it again terminates and so on. The function isn't running consistently, the interval timer (the WebAPI that calls for the function to be invoked) is.
And what if the foo() function don't have the setInterval() method,
but just many lines of code that get executed and I want to stop it
from executing after a specific event.
Your question seems to imply that you want to stop a function that is currently executing when another event takes place. This can't really happen in JavaScript since JavaScript is a single-threaded environment. Any event can only be raised and handled after all other code is done processing. So, there really can't ever be a scenario like the one you mention, unless we are talking about asynchronous code. Asynchronous code is code that runs outside of the JavaScript runtime. With that kind of code, you can send a message to the WebAPI that is processing that external code that you would like to cancel/abort that processing and that is what we're doing when we call clearInterval().
See below:
document.getElementById("start").addEventListener("click", startInterval);
document.getElementById("stop").addEventListener("click", stopInterval);
// You'll need a variable to store a reference to the timer
var timer = null;
function startInterval() {
// Then you initilize the variable
timer = setInterval(function() {
console.log("Foo Executed!");
}, 1500);
}
function stopInterval() {
// To cancel an interval, pass the timer to clearInterval()
clearInterval(timer);
}
<button type="button" id="start">Start</button>
<button type="button" id="stop">Stop</button>
For that use return; in the place you want to kill the process
I have a function called using setInterval of JavaScript, which in some scenarios is called multiple times without the interval gap defined (I suspect this is because the intervals are not cleared properly and I'm creating multiple intervals, but I'm not sure).
I can not reproduce the problem locally.
The code uses Twirl but it's basically JS:
function refreshCheckInRequests() {
if (interval) { // If there is an interval running stop it.
clearInterval(interval);
}
jsRoutes.controllers.ExtranetSecuredController.findPendingCheckInRequests("#gymId").ajax({ // Ajax call using Play Framework
success: function (data) {
$("#checkin-request-container").html(data);
addRowListeners()
},
error: function (data) {
if (data.status == 401) {
errorSwitchGym("#Messages("extranet.switch.gym")");
//location.reload();
}
else {
unexpectedError(data)
}
},
complete: function() {
interval = initInterval(); // At the end of the call init the interval again
}
});
}
function initInterval() {
return setInterval(function () { refreshCheckInRequests(); },
20000);
}
var interval;
refreshCheckInRequests();
$("#checkin-request-refresh").click(function (event) {
refreshCheckInRequests();
event.preventDefault();
});
I could use setTimeout instead because at the end, I always call refreshCheckInRequests once, I stop the interval, and at the end I create a new one.
If I use timeout I have to call again my function at the end of the execution of the callback of timeout (like I'm doing right now). If something goes wrong, my callback will never be called again.
Anyway, I would like to know what's going on here. Am I missing something? Am I doing something wrong? Any suggestions?
You're clearing the current interval every time refreshCheckInRequests is called, but there is a delay between when refreshCheckInRequests is called and the new interval is assigned. Because refreshCheckInRequests also runs when an element is clicked, the following scenario could result in an unterminated interval:
User clicks, current interval is cleared, asynchronous findPendingCheckInRequests runs
User clicks again, no interval currently exists (nothing to clear), another asynchronous findPendingCheckInRequests runs
Response from first findPendingCheckInRequests comes back. complete handler runs, interval is assigned to the new interval
Response from second findPendingCheckInRequests comes back. complete handler runs, interval is assigned to the new interval over the old interval
The first created interval remains running, but there no longer exists a reference to it, so that first interval continues repeating forever.
So, try clearing the interval at the moment you reassign interval, ensuring that every new interval will always clear the old one, if an old one is running:
complete: function() {
clearInterval(interval);
interval = initInterval();
}
I always run into this problem and seem to implement a nasty looking solution.
It seems like a common design pattern to fire an action immediately, but not let that action queue up if clicked rapidly / delay firing if previously called within a timeframe. In my real world example, I have an AJAX call being made, so if I don't prevent repetitive actions the browser queues requests.
How would you implement this differently? What other options are there?
function myFunction() {
console.log("fired");
}
var timeout = null;
$("#foo").click(function() {
// if not previously clicked within 1 second, fire immediately
if (!timeout) {
myFunction();
timeout = setTimeout(function() {
timeout = null;
}, 1000);
} else {
// clicked again within 1s
clearTimeout(timeout); // clear it - we can't have multiple timeouts
timeout = setTimeout(function() {
myFunction();
timeout = null;
}, 1000);
};
});
With your current code, if you repeatedly click "#foo" at an interval slightly less than one second, say every 800ms, on first click it will fire the function immediately (obviously), but then it will fire the function exactly once more one second after the last click. That is, if you click ten times at 800ms intervals the function will fire once immediately and a second time approximately 8 seconds (800ms * 9 + 1000ms) after the first click.
I think you're better off removing the else case altogether, so that on click it will fire the function if it has not been called within the last second, otherwise it will do nothing with no attempt to queue another call up for later. Not only does that seem to me like a more logical way to operate, it halves the size of your function...
On the other hand, since you mentioned Ajax, rather than disabling the function based on a timer you may like to disable the function until the last Ajax request returns, i.e., use a flag similar to your timerid and reset it within an Ajax complete callback (noting that Ajax complete callbacks get called after success or failure of the request).
In the case of an auto-complete or auto-search function, where you want to send an Ajax request as the user types, you might want to remove the if case from your existing code and keep the else case, because for auto-complete you likely want to wait until after the user stops typing before sending the request - for that purpose I'd probably go with a shorter delay though, say 400 or 500ms.
Regarding general structure of the code, if I wanted a function to be fired a maximum of once per second I'd likely put that control into the function itself rather than in a click handler:
var myFunction = function() {
var timerid = null;
return function() {
if (timerid) return;
timerid = setTimeout(function(){ timerid=null; }, 1000);
// actual work of the function to be done here
console.log("myFunction fired");
};
}();
$("#foo").click(function() {
myFunction();
});
The immediately invoked anonymous function that I've added makes it uglier, but it keeps the timerid variable out of the global scope. If you don't like that obviously you could simply declare timerid in the same scope as myFunction() as you currently do.
This answer is getting kind of long, but if you have a lot of different functions that all need some kind of repeat control in them you could implement a single function to handle that part of it:
function limitRepeats(fn, delay) {
var timerid = null;
return function() {
if (timerid) return;
timerid = setTimeout(function(){ timerid = null; }, delay);
fn();
};
}
// myFunction1 can only be called once every 1000ms
var myFunction1 = limitRepeats(function() {
console.log("fired myFunction1()");
}, 1000);
// myFunction2 can only be called once every 3000ms
var myFunction2 = limitRepeats(function() {
console.log("fired myFunction2()");
}, 3000);
$("#foo").click(function() {
myFunction1();
myFunction2();
});
There are several divs and handler to send ajax requests when they are clicked. My problem is that i don't know how to force my handler not to exceed limit of 1 request per 30 seconds.
Appreciate your help!
The excellent Underscore.js has a throttle function. You pass in the handler that you want to throttle and get back a rate-limited version of the same function.
var throttled = _.throttle(someHandler, 100);
$(div).click(throttled);
http://documentcloud.github.com/underscore/#throttle
Here's a simplified version that I've used in my own code:
function throttle(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
if (!timeout) {
// the first time the event fires, we setup a timer, which
// is used as a guard to block subsequent calls; once the
// timer's handler fires, we reset it and create a new one
timeout = setTimeout(function() {
timeout = null;
func.apply(context, args);
}, wait);
}
}
}
A good way to test it is by firing off a bunch of scroll events and watching your handler log to the Firebug console:
document.addEventListener("scroll", throttle(function() {
console.log("test");
}, 2000), false);
Here's a version that limits click-events on divs to once every 30 seconds, as requested (requires jQuery):
$("div").click(throttle(function() {
// ajax here
}, 30000));
If you want to rate limit, then unfortunately the _.throttle method that underscore.js provides is not your solution. Throttle will simply ensure your method is never called more than X seconds, and therefore all subsequent function calls will be disregarded until that period has passed.
If you want to rate limit so that you never call your function more than X times per second, but don't lose those function calls altogether, then you need a wholly different solution.
I have written an underscore extension at https://gist.github.com/1084831
You can see a working example at http://jsbin.com/upadif/8/edit#preview
Create a boolean canFireRequest, or whatever, flag and set it to false after each ajax request. Then create a 30 second time span that sets it back to true; check the flag's value before each new request.
Here's a rough example:
if ($(this).data('canFireRequest')) {
// Ajax request goes here
$(this).data('canFireRequest', false);
}
setTimeout(function() {
$(this).data('canFireRequest', true)
}, 30000);
I've got a sequence of Javascript function calls in a function I have defined to be executed when a web doc is ready. I expected them to be executed in sequence, as one ends the next begins, but the behaviour I see doesn't match up with that.
Additionally there is manipulation of the graphical components going on in between the calls (for example, I add in a checkpoint time to draw on a div on the page inbetween each of the mentioned calls) but those redraws aren't happening in sequence... they all happen at once.
I'm a bit of a n00b with the whole javascript-in-the-browser thing, is there an obvious mistake I'm making, or a good resource to go find out how to do this stuff?
Update - sample
// called onReady()
function init() {
doFirstThing();
updateDisplayForFirstThing();
doSecondThingWithAjaxCall();
updateDisplayForSecondThing();
...
reportAllLoaded();
}
IE won't update the display until the current script is finished running. If you want to redraw in the middle of a sequence of events, you'll have to break your script up using timeouts.
If you post some code we can help refactor it.
edit: here's a general pattern to follow.
function init() {
doFirstThing();
updateDisplayForFirstThing();
}
function updateDisplayForFirstThing() {
// existing code
...
// prepare next sequence
var nextFn = function() {
// does this method run async? if so you'll have to
// call updateDisplayForSecondThing as a callback method for the
// ajax call rather than calling it inline here.
doSecondThingWithAjaxCall();
updateDisplayForSecondThing();
}
setTimeout(nextFn, 0);
}
function updateDisplayForSecondThing() {
// existing code
...
// prepare next sequence
var nextFn = function() {
// continue the pattern
// or if you're done call the last method
reportAllLoaded();
}
setTimeout(nextFn, 0);
}
This can be fixed for many cases by using callbacks, especially with AJAX calls -- for example:
function doFirstThing(fn){
// doing stuff
if(typeof fn == 'function') fn();
}
function updateDisplayForFirstThing(){
// doing other stuff
}
function init(){
doFirstThing(updateDisplayForFirstThing);
}
Another option is to use return values:
function doFirstThing(fn){
// doing stuff
if(x) return true;
else return false;
}
function updateDisplayForFirstThing(){
// doing other stuff
return true;
}
function init(){
if(doFirstThing()){ updateDisplayForFirstThing(); }
}
setting timeouts to step through your code is not really a good way to fix this problem because you'd have to set your timeouts for the maximum length of time each piece of code could possibly take to execute.
However, you may still sometimes need to use a setTimeout to ensure the DOM has properly updated after certain actions.
If you end up deciding that you would like some JavaScript threading, check out the still being drafted Web Workers API. Browser support is hit and miss though the API is implemented in most modern web browsers.
Question: exactly how did you go about determining when the "doc is ready"? The DOMContentLoaded event isn't supported in IE I'm fairly certain... if you're in need of waiting for your document to load in its entirety you could use something like this:
var onReady = function(callback) {
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", callback, false);
return true;
} else if (document.attachEvent) {
var DOMContentLoaded = function() {
if (document.readyState === "complete") {
document.detachEvent("onreadystatechange", DOMContentLoaded);
onReady();
}
};
return true;
}
};
Then of course you'll need to develop a setTimeout testing for some flags state indicating the page is loaded upon completion before continuing the execution of the rest of your code... that or any number of other methods...
Or you could just include the script at the bottom of your body...
I'm just rambling though until you have some code to show us?