calling a Javascript anonymous function right when it's declared, doesn't work, calling it later does

11,384

Solution 1

requestAnimationFrame expects a function, but in your code, step is not a function, it is undefined because you don't return any value from your self-invoking function.

var step = (function(){
    // this code is executed immediately, 
    // the return value is assigned to `step` 
})();

If you remove the calling parenthesis, then step is indeed a function.

Please see @Martin's comment to this answer. I was referring to the fact that step is undefined after the function is executed, but of course it is also undefined when you invoke the function the first time.

Solution 2

I see some fundamental misunderstanding of what's going on here. For example, in your first declaration:

var requestAnimationFrame = ( function() {
    return window.requestAnimationFrame || //Chromium 
    window.webkitRequestAnimationFrame || //Webkit
    window.mozRequestAnimationFrame || //Mozilla Geko
    window.oRequestAnimationFrame || //Opera Presto
    window.msRequestAnimationFrame || //IE Trident?
    function(callback) { //Fallback function
        window.setTimeout(callback, 1000/60);
    }
})();

You're creating an anonymous function, then immediately calling it and assigning the result to a variable. I don't see the point of this. The following would work equally well:

var requestAnimationFrame = 
    window.requestAnimationFrame || //Chromium 
    window.webkitRequestAnimationFrame || //Webkit
    window.mozRequestAnimationFrame || //Mozilla Geko
    window.oRequestAnimationFrame || //Opera Presto
    window.msRequestAnimationFrame || //IE Trident?
    function(callback) { //Fallback function
        window.setTimeout(callback, 1000/60);
    };

There's no anonymous function now (well except for the little fallback function), it's just code that runs. You can apply a similar simplification to your step() function.

Solution 3

Among the issues is this (corrected):

var step = function(){
    now = new Date().getTime();
    hits += 1;
    if( now - last >= 1000 ){
        last += 1000;
        console.log( "fps: "+ hits );
        hits = 0;
    }
    requestAnimationFrame( step );
};

Solution 4

Your current code essentially says "run this anonymous function and assign its return value to step". There are two basic problems with this:

  1. The function doesn't return a value, so even after it runs step will be undefined.
  2. Even if the function did return a value, you are trying to use step inside the function the first time it runs, at which point the assignment to step has not yet taken place.

The simplest way to fix this is what you already did, i.e., declare step as a function and then run it on the next line:

var step = function() { ... };
step();

Or you could use a named function expression:

(function step() {
   ...
   requestAnimationFrame( step );
})();

Which is the equivalent of:

(function () {
    ...
    requestAnimationFrame( arguments.callee );
})();

Unfortunately IE isn't that great at named function expressions.

And also unfortunately (unfortunate from my point of view, anyway) arguments.callee is now deprecated and won't work in strict mode.

Solution 5

I see a couple of issues. You are assigning step the return value of the anonymous function. Whereas, when you remove the parentheses. You are making step a function. Since you are not returning a value in the anonymous function, step is undefined. Therefore, you will get a type error. I would remove the parentheses at the end.

Share:
11,384
Petruza
Author by

Petruza

General software engineer, golang advocate, also typescript, C, C++, GDScript dev. Interested in emulation, video games, image processing, machine learning, computer vision, natural language processing, web scraping.

Updated on June 06, 2022

Comments

  • Petruza
    Petruza almost 2 years

    [answered]

    I'm testing my browser's fps for an html5 game.
    I have this code:

    var requestAnimationFrame = ( function() {
        return window.requestAnimationFrame || //Chromium 
        window.webkitRequestAnimationFrame || //Webkit
        window.mozRequestAnimationFrame || //Mozilla Geko
        window.oRequestAnimationFrame || //Opera Presto
        window.msRequestAnimationFrame || //IE Trident?
        function(callback) { //Fallback function
            window.setTimeout(callback, 1000/60);
        }
    })();
    
    var hits = 0;
    var last = new Date().getTime();
    
    var step = (function(){
        now = new Date().getTime();
        hits += 1;
        if( now - last >= 1000 ){
            last += 1000;
            console.log( "fps: "+ hits );
            hits = 0;
        }
        requestAnimationFrame( step );
    })();
    

    It gives the following error on Chrome:
    Uncaught Error: TYPE_MISMATCH_ERR: DOM Exception 17
    On line #27: requestAnimationFrame( step );

    W3 says this error is: If the type of an object is incompatible with the expected type of the parameter associated to the object.
    But I'm not actually interacting with the DOM at all, except for window

    But if I remove the calling parentheses of the anonymous function assigned to step and instead just declare that function and on a new line I put:
    step();

    It works.
    Why is this?
    Shouldn't both work the same?

  • Martin Jespersen
    Martin Jespersen over 12 years
    +1 And even if step wasn't undefined at the point it gets called, it would still be undefined after since the immediate function doesn't return anything.
  • Felix Kling
    Felix Kling over 12 years
    @Martin: Right, I was actually referring to this issue and did not even notice that it is also undefined when the function is invoked. Thanks!
  • Felix Kling
    Felix Kling over 12 years
    You are right, it is not necessary to use an anonymous function here. I assume the code is some variant of: paulirish.com/2011/requestanimationframe-for-smart-animating
  • Greg Hewgill
    Greg Hewgill over 12 years
    @FelixKling: Good catch. Copy-paste programming rules!
  • Petruza
    Petruza over 12 years
    You're right, I was a victim of copy & paste, but what you say makes more sense.
  • Petruza
    Petruza over 12 years
    Thanks! it is so obvious now, but I didn't see it. The DOM error distracted me.
  • Jonathan M
    Jonathan M over 12 years
    You had step assigned to the result of a function, instead of a function itself. My correction here assigns the function to step.
  • Petruza
    Petruza over 12 years
    Oh right, your comment would have explained it better than the code. Thanks!