35

This is my first real dive into JavaScript. Sure I've used it before, but I've never really written anything from scratch.

Anyway, I'm having a very strange problem that I hope someone can figure out for me.

I'm trying to make the text from a div fade from black to white. Simple, yeah?

The following code works. It will change the color to white, however, the setTimeout time of 500ms is being ignored.

If you use Chrome and look at the JS console, you'll easily see that the doFade() method is being called almost instantaneously, not every 500 milliseconds.

Can anyone explain this?

var started = false;
var newColor;
var div;
var hex = 0;

function fadestart(){
    if (typeof fadestart.storedColor == 'undefined') {
        div = document.getElementById('test');
        fadestart.storedColor = div.style.color;
    }
    if(!started){
        console.log('fadestart');
        newColor = fadestart.storedColor;
        started = true;
        setTimeout(doFade(), 500);
    }
}

function fadestop(){
    console.log('fadestop');
    div.style.color = fadestart.storedColor;
    started = false;
    hex = 0;
}

function doFade(){
    if(hex<=238){
        console.log(hex);
        hex+=17;
        div.style.color="rgb("+hex+","+hex+","+hex+")";
        setTimeout(doFade(), 500);
    }
}
Jonah H.
  • 1,760
  • 4
  • 18
  • 31

3 Answers3

65

You need to get rid of the parentheses on doFade().

The parentheses invoke the function instantly.

Just use this instead: doFade

KeatsKelleher
  • 10,015
  • 4
  • 45
  • 52
  • Actually, you want 'dofade()' as a string, in quotes. You got the root cause, though. – Surreal Dreams Nov 08 '10 at 02:03
  • Yes `func` evaluates to the function itself while `func()` call the function and evaluates to its return value. – Alex Jasmin Nov 08 '10 at 02:03
  • 2
    pretty sure you don't need quotes. You should be able to put either doFade or "doFade". I think there might even be slight performance benefits to not converting the string "doFade" to the function handle. – KeatsKelleher Nov 08 '10 at 02:04
  • 3
    @Surreal Actually, you want to pass the function without parentheses. Quoting it executes the passed code snippet in the global context, which can have vastly different results. – deceze Nov 08 '10 at 02:05
  • 6
    @Surreal Dreams setTimeout accepts function **or** a string to be eval'd as the callback. Functions in Javascript are first class objects and can be (often are) passed around freely around as such. As Javascript is incredibly dynamic, eval can (and should) almost always be avoided. – Angiosperm Nov 08 '10 at 02:09
  • @deceze a function passed to setTimeout is still executed in the global context, as setTimeout provides no way to change the receiver of the callback (and trying this with setTimout.call/apply throws in exception). While I agree that you shouldn't pass a quoted string, your reasoning is wrong. – Angiosperm Nov 08 '10 at 02:15
  • @Angio I mean that if a function is not callable from the global scope, e.g. because it was created non-publically in its own scope, `setTimeout("foo()", 500)` may not be able to call anything, or it will call the wrong function. I'm not talking about `.call` context at the moment. – deceze Nov 08 '10 at 02:20
  • @deceze Ah ok thats much clearer :) – Angiosperm Nov 08 '10 at 02:29
  • 2
    @akellehe, @deceze & @Angio: Thanks for correcting me. I felt *reasonably* confident about my answer, but I knew if I was wrong you guys would be all over me and I'd learn it right. :) So thanks. – Surreal Dreams Nov 08 '10 at 02:48
  • 1
    Why do they have it execute immediately with parens? Is it so that you can have another function return the function to use as the timeout dynamically? – CommaToast Jan 27 '14 at 22:24
  • @CommaToast: That is indeed the effect, but the reason is not specifically to enable you to do that. Rather it's a very general matter of syntax. If you write `a(b(), c())` in javascript (as in most languages), it means "compute the return values of b() and c(), and then pass those as parameters to a()". This is true even in the case where "a" is "setTimeout". – Doin May 12 '16 at 18:29
  • Anyone got documentation on this? – CrandellWS Jan 05 '17 at 18:18
  • how to pass parameters then? – Shrikant Dande May 29 '18 at 13:33
  • @ShrikantDande you can define your function with a closure. The function defining your function can accept parameters and return your function, which is passed to `setTimeout` – KeatsKelleher May 30 '18 at 13:41
  • actually I am passing function to dispatch , which is not working `setTimeout(() => { dispatch(actions.success(mock_Response, dataKey)); }, 1000); ` – Shrikant Dande May 31 '18 at 11:13
24
setTimeout(doFade(), 500);

This line says "execute doFade(), then pass whatever value it returns to setTimeout, which will execute this return value after 500 milliseconds." I.e., you're calling doFade() right there on the spot.

Skip the parentheses to pass the function to setTimeout:

setTimeout(doFade, 500);
deceze
  • 510,633
  • 85
  • 743
  • 889
4

I think you should use setTimeout(doFade, 500); or setTimeout("doFade()", 500);

PeterWong
  • 15,951
  • 9
  • 59
  • 68
  • 1
    The quoted call also works but is awful. See akellehe's answer for details. – ThiefMaster Nov 08 '10 at 02:08
  • Yes of course. My habit is passing a function or even an inline function. But quoting it is always another choice (of course not a better choice in most cases). – PeterWong Nov 08 '10 at 02:14
  • Nice one, the quoted function seems to be the only choice if passing a parameter. – asoundmove Oct 05 '16 at 13:08
  • Just noticed this one has a better answer: http://stackoverflow.com/questions/3800512/calling-functions-with-settimeout – asoundmove Oct 05 '16 at 13:11