views:

64

answers:

4

I'd like to build a function that returns false if it's been called less that half a second ago.

timething.timechill=function(){
    var last
    if (last){
            if ((now.getTime()-last)>500){
                    return true
            }
            else{

                    return true
            }
    }
    else {
            last=now.getTime()
            return false
    }}

Any ideas? I'd like to avoid setTimeout() and ignore input if it's coming too quick to avoid overflow. Is this a good practice?

A: 

Your function as defined will always return false because the last variable is never saved anywhere. You could hold it as a property of an object, or you can hold it within a closure.

Here's a closure example:

timething.timechill = (function() {
    var last = 0;

    function timechill() {
        var now;

        now = new Date().getTime();
        if (last) {
            if (now - last > 500) {
                // It's been long enough, allow it and reset
                last = now;
                return true;
            }
            // Not long enough
            return false;
        }

        // First call
        last = now;
        return false;
    }

    return timechill;
})());

That uses an anonymous scoping function to build your timechill function as a closure over the last variable. The anonymous scoping function returns a reference to the timechill function, which gets assigned to timething.timechill. Nothing other than the timechill function can access last, it's entirely private.

(I'm sure the actual logic of the function could be refactored a bit, but I think that's pretty close to your original except there was one place you were returning true where I think you wanted false.)

Whether this is a good idea depends entirely on your use-case. I wouldn't busy-loop on the above. :-) But if you're using it to pop up something like SO's "You can only rate a comment once every five seconds" things, it would be fine, although in that case I'd probably generalize it.

T.J. Crowder
Thanks for the answer. I'm trying to avoid "stacking" multiple mouse wheel events and having them "roll on" I've tried all the solutions here but they all break my code for reasons I'm scratching my head over.At the end you have "})());" which I'm trying to understand. Eclipse tells me "invalid assignment left hand side" and my attempts to fix it haven't worked .. yet
heorling
A: 
timething.timechill = (function () {
    var lastCall = 0;
    return function () {
        if (new Date() - lastCall < 500)
            return false;
        lastCall = new Date();
        //do stuff
    }
})();

The idea here is that (function() { ... })(); will create an anonymous function and run it immediately. timething.timechill is not assigned this function. Instead, it is assigned the inner function returned by this function.

Note that lastCall is not declared (using the var keyword) within that inner function. And when the outer function returns, lastCall doesn't disappear because the inner function has "enclosed" it by virtue of the fact that it refers to the variable.

When you run timething.timechill later and it encounters this variable, it will search outside the function's scope for the variable and find the one that was declared earlier. When it returns, the variable still does not disappear, since it was declared outside the function's scope.

It is hard to explain this concept clearly, but it is very useful because lastCall is invisible to the rest of your code which doesn't need to see it.

James M.
Thanks! I ended up using your code with a minor modification. I inserted this else statement:else{ lastCall = new Date(); return true; }
heorling
The question is, will the Date() call cause problems later on? It only get's called in special cases. Is kgiannakakis right? Is setTimeout the way to solve this?
heorling
`setTimeout` is generally better than polling. But if you're already polling, I don't think this will cause extra problems. It could be optimized slightly with `var current = new Date();` before the if-statement, then using the same `current` for the comparison and assignment instead of creating two `Date` objects. Firefox has a static `Date.now()` method that presumably doesn't create a whole object, but you'd need a workaround for most browsers. See https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/now
James M.
A: 

I don't think this is a good idea. Depending on how you call this method, it may lead to an "infinite loop" behavior. With setTimeout you have asynchronous operation - you don't block the browser, while waiting for time to pass. Most browsers will detect blocking code and disable your script.

kgiannakakis
That's not good is it... If I understood setTimeout correctly, it simply waits to execute the script right? I need to "cancel" it if it's too soon, not delay it.. Am I missing something?
heorling
A: 

The "last" variable has to be stored in another object, such as the window object for a global variable or the timething object here. And I have never heard of a "now" object!?

timething.timechill = function(){

    if (!timething._last_timechill){

       if ((new Date())-timething._last_timechill >= 500) return true;
       else return false;

    } else {

        timething._last_timechill = new Date();
        return false;

   }

}

You can replace "timething" with "window" in the function if you like.

EDIT: As the others pointed out, you can alsostore your _last_timechill variable in a closure.

Vincent