0

I have a loop, a setTimout and a callback. I need to use an anonymous function to keep the variables correct in the callback.

I want to have the callback as a separate function because it is too large to have in the loop.

This does not work:

for (var i = 0; i < 10; i++) {
  setTimeout(callback, 1000*i, i);
}

var callback;
(callback = function(i) {
  console.log(i);
})();

How can define an anonymous function that I can call from setTimeout?

EricC
  • 5,720
  • 13
  • 52
  • 71
  • Just a heads up, your 10 setTimeouts you have created will all execute in 1 second, and not over a period of 10 seconds. Is that what your meaning to do? – Keith Mar 05 '18 at 19:34
  • Why have you wrapped the callback definition in an IIFE? – Oliver Charlesworth Mar 05 '18 at 19:34
  • Just `function callback (i) { console.log(i) }` is enough for your case, since third parameter to `setTimeout` makes sure that `i` is correct. – dfsq Mar 05 '18 at 19:42
  • http://brackets.clementng.me/post/24150213014/example-of-a-javascript-closure-settimeout-inside – sumit Mar 05 '18 at 19:44
  • https://stackoverflow.com/a/25267864/1048572 – Bergi Mar 05 '18 at 20:11
  • 1
    Just use `let i` and define the anonymous callback function inside the block scope of the loop – Bergi Mar 05 '18 at 20:12
  • @Keith, you are correct, I was too quick, it should have been `callback, 1000*i, i)`, so it prints out one increasing number each second for ten seconds. – EricC Mar 05 '18 at 20:30

5 Answers5

1

It appears that you don't need anything more complex than this:

function callback(i) {
  console.log(i);
};

for (var i = 0; i < 10; i++) {
  setTimeout(callback, 1000*i, i);
}

You had two issues:

  • You tried to use callback before you'd defined it.
  • You wrapped your function in an IIFE (for no apparent reason), which means it would have been invoked once with an undefined argument.
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • Your immediately invoking the callback, you may as well not use the setTimeout in the first place.. : – Keith Mar 05 '18 at 19:42
  • yes, this is more then enough, since third param to `setTimeout` already takes care of it. – dfsq Mar 05 '18 at 19:43
  • That it's :) the parameter value. I personally still prefer the closure route, just out of consistency for everything else were async is used. New ES6 `let` solves all of this a lot simpler. – Keith Mar 05 '18 at 19:44
  • I missed a *i in the question, it should have been `setTimeout(callback, 1000*i, i)`... – EricC Mar 05 '18 at 20:33
  • Ok, I have probably misunderstood something. I was under the impression that `i` would be 9 by the time the callback is executed, and I, therefore, would need an anonymous function. Why is that not needed here? – EricC Mar 05 '18 at 20:40
  • Sorry, I just realized my error. setTimout already handles this problem. – EricC Mar 05 '18 at 20:42
  • @EricC - `setTimeout` isn't doing anything special, it's just that you're not capturing any variables anywhere (because there are no closures), so the problem you're alluding to simply doesn't exist here. – Oliver Charlesworth Mar 05 '18 at 20:43
1

If I understand correctly, it would seem to me that it's more logical to use setInterval() than setTimeout() in conjunction with a for-loop.

I've created a callback function using a closure to keep track of the counter variable in my example:

function init() {
  var increment = initCounter();

  setInterval(function() {
    console.log(increment());
  }, 1000);
}


function initCounter() {
  var i = 0;
  return function() {
    return ++i;
  }
}

init();
Tom O.
  • 5,730
  • 2
  • 21
  • 35
1

Just put the for-loop after the function expression.

This is following your approach.

var callback;
(callback = function(i) {
  if (i !== undefined)
    console.log(i);
})();

for (var i = 0; i < 10; i++) {
  setTimeout(callback, 1000, i);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

Better approach using function declaration:

function callback(i) {
    console.log(i);
};

for (var i = 0; i < 10; i++) {
  setTimeout(callback, 1000, i);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ele
  • 33,468
  • 7
  • 37
  • 75
1

Using ES6 Arrow Functions:

let callback = (i) => {
  console.log(i);
}

for (var i = 0; i < 10; i++) {
  setTimeout(callback, 1000, i);
}
Jimmy Leahy
  • 566
  • 3
  • 14
0

I'm missing something, you're question is how to call an anonymous function, yet every single answer (including your own question) has included named functions.

What's wrong with:

for (var i = 0; i < 10; i++) {
  setTimeout(function(m) { console.log(m); }, 1000*i, i);
}
Adam Jenkins
  • 51,445
  • 11
  • 72
  • 100