20

Perhaps an odd question but here it goes: I have a function which I call periodically and within that function I need to know which iteration I'm in, or how many times the function has been called. A simplified version of the problem:

jQuery( document ).ready( function(){
    setInterval( "myFunction()", 3000 );
});

function myFunction()
{
    alert( "I have been called X times" );
}

So, how do I figure out the X in the above code?

Weblurk
  • 6,562
  • 18
  • 64
  • 120

10 Answers10

33

Easy version: make a global variable like in codeling's answer. The problem - if some other code also defines a global variable with the same name, you're both in trouble.

Easy extended version - give the variable a crazy name that nobody will ever use: calledTimesED7E69A7B141457CA8908A612E3D7A3A

Clever version: append that variable to an existing global variable. Remember - everything's an object in Javascript!

$(function(){ setInterval(myFunction, 3000); });

function myFunction()
{
    myFunction.calledTimes++;
    alert( "I have been called " + myFunction.calledTimes + " times" );
}
myFunction.calledTimes = 0;

Traditional version: use scoping to hide that variable.

$(function()
{
    var calledTimes = 0;
    setInterval(function()
    {
        calledTimes++;
        alert( "I have been called " + calledTimes + " times" );
    }, 3000); 
});

This hides "myFunction" though, so let's try again with a tricky kind of scoping:

var myFunction = null;
(function()
{
    var calledTimes = 0;
    myFunction = function()
    {
        calledTimes++;
        alert( "I have been called " + calledTimes + " times" );
    } 
})();

$(function () { setInterval(myFunction, 3000); });

... and there are a zillion other ways you would hide that variable with scoping. Just pick your favorite.

Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • 3
    It still amazes me how much time and effort people put into answering questions here. If I could vote this answer up a thousand times I would :) Thanks for the extensive yet simply explained answers! – Weblurk Dec 15 '11 at 09:24
24

You could simply use a global variable, which is increased each time you call the function:

var myFuncCalls = 0;

function myFunction()
{
    myFuncCalls++;
    alert( "I have been called " + myFuncCalls + " times" );
}

As soon as your code gets a little more complex (or if you use a lot of other libraries), you should, however, consider using scoping as shown in the other answers here (best explained in the one by Vilx).

Community
  • 1
  • 1
codeling
  • 11,056
  • 4
  • 42
  • 71
  • Will this work even if the function is inside a separate file? Will that function have "knowledge" of the myFunCalls variable? – Weblurk Dec 15 '11 at 09:13
  • In that case it's probably best if you also declare the variable in the separate file, it will be visible in your "main" document. I'm not 100% sure about the other way round – codeling Dec 15 '11 at 09:14
  • Yes, all the javascript files are sort of "copy-pasted" in the main document before they're executed. So they all share the same global variables and everything. That's why you should always be careful about making global variables - if two scripts make the same global variable, they'll bother each others work. – Vilx- Dec 15 '11 at 09:22
  • True. Not a problem in this case though. – Weblurk Dec 15 '11 at 09:27
  • 2
    @FoadFarkhondeh You should be very careful with a solution like this, as global namespace pollution is often a bad practice. – Yoshi Dec 15 '11 at 09:28
  • I think Vilx-'s answer is better not only because he explain how to do it Foad's case, but also because he explains scope, which is the more clever way to do such a counter I think. – thibaultcha Oct 20 '13 at 14:44
10

Here's another interesting solution that doesn't use an external variable. The best part about this is you can leave any pre-existing functions untouched and call them as you would normally. That means if you're attempting to "tap in" to an function in an existing library, this will work very well for you. It adds an unobtrusive counter and allows you to continue calling existing functions normally; even with arguments!

// no js library required

// pre-existing function
var a = function(){
    console.log("pre-existing function function");
    console.log("arguments:", arguments);
};

// add counter func
var addFnCounter = function(target){
    var swap = target;
    var count = 0;
    return function(){
        swap.apply(null, arguments);
        count++;
        console.log("func has been called " + count + " times");
        console.log("\n");
    };
};

// usage
a = addFnCounter(a);

// call a() as you would normally
a();
a(1,2,3);
a('hello', 'world');

// using your setInterval example
setInterval(a, 3000);

Output

pre-existing function function
arguments: []
func has been called 1 times

pre-existing function function
arguments: [1, 2, 3]
func has been called 2 times

pre-existing function function
arguments: ["hello", "world"]
func has been called 3 times

setInterval output

pre-existing function function
arguments: []
func has been called 4 times

pre-existing function function
arguments: []
func has been called 5 times

pre-existing function function
arguments: []
func has been called 6 times

See it working here on jsfiddle

maček
  • 76,434
  • 37
  • 167
  • 198
  • 2
    For anyone wondering, a function takes another function and returns a modified version is called a **Decorator**. – aljgom Mar 14 '17 at 19:37
9

You'll have to use a closure. Normally you would use a static variable. in Javascript it would look something like:

jQuery( document ).ready( function(){
    setInterval( myFunction, 3000 );
});

var myFunction = (function(){
    var count = 0;
    return function(){
         count++
         alert( "I have been called " + count + " times");
    }
})();

Demonstration: http://jsfiddle.net/MZQ83/2/

Ben West
  • 660
  • 5
  • 17
2

A static variable is cleaner and it won't pollute your outer scope either, compared to a closure or a decorator as in other answers.

var foo = function(){
    alert( ++foo.count || (foo.count = 1) );
}


// test
function callTwice(f){ f(); f(); }
callTwice(foo)                  // will alert 1 then 2

or

callTwice( function bar(){           
    alert( ++bar.count || (bar.count = 1) );
});                             // will alert 1 then 2

the second one is a named anonymous function. And note this syntax:

var foo = function bar(){ /* foo === bar in here */ }
aljgom
  • 7,879
  • 3
  • 33
  • 28
0

ES6 / ES2015

You can use a Proxy for your function utilising the apply() trap:

const addCounter = fn => {
  let count = 0; // keep count

  //define handler
  const handler = {
    apply() {
      //do something with this counter
      console.log(`I have been called ${++count} times `); 
      
      return Reflect.apply(...arguments); //call the function normally
    }
  }
  
  //wrap the function into a proxy that uses the handler and return it
  return new Proxy(fn, handler);
}

setInterval( addCounter(myFunction), 1000 );


function myFunction() { //sample operation - move an image
  const img = document.querySelector("img");

  let offset = img.offsetLeft + 10;
  if (offset > 100) //return to start
    offset = 0;
    
  img.style.left = `${offset}px`;
}
img {
  position: absolute;
}

.as-console-wrapper {
  max-height: 45px !important;
}
<img src="https://picsum.photos/150" />
VLAZ
  • 26,331
  • 9
  • 49
  • 67
0

You can use an Immediately Invoking Function Expression (or IIFE) to create a closure around the counter function. You can do it like this with ES6 Arrow functions:

const counterFunction = (() => {
  let counter = 0;
  return () => console.log(++counter);
})();

counterFunction();
counterFunction();
counterFunction();

Or with normal function expressions:

var counterFunction = (function() {
  var counter = 0;
  return function() {
    console.log(++counter);
  };
})();

counterFunction();
counterFunction();
counterFunction();

Read more about IIFEs
Read more about closures

Jacob
  • 1,577
  • 8
  • 24
0

There is an inbuilt function in JS called console.count()

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
0

My approach would add a property “count” to the function itself.

Just add one line at the beginning of your function you want to have tracked calls.

function func() {
  func.count = (func.count || 0) + 1;
  // first time you call the function func.count is undefined so use 0 instead
  console.log("hi");
}

func();
console.log(func.count) // 1
func();
func();
func();
console.log(func.count) // 4

Functions are objects in javascript after all. No pollution of global namespace, no wrapping or closures needed, very simple to understand and to write.

Kvetoslav
  • 443
  • 3
  • 15
0

Create a global variable and initialize by zero. then increment by one when myfunction() called. Display that variable instead of X.

Dhinakar
  • 4,061
  • 6
  • 36
  • 68