0

How do I bind (or pass a value) to a callback at the time the callback function is passed to the caller?

For instance:

function callbackTest() {
    for(i=0; i<3; i++){
       setTimeout(function(){
            console.log("Iteration number ", i); 
        }.bind(i), i*1000);
    }
}

callbackTest();

Results in:

$ node callback-with-bind.js
Iteration number  3
Iteration number  3
Iteration number  3

I would expect the binding to happen at the time the callback is passed to the caller. But no.

How do I do it?

In my actual application, the caller passes another param to my callback, so I can't simply pass i as a param.

function callbackTest() {
    for(i=0; i<3; i++){
        setTimeout(function(myParam){
            console.log("Iteration number ", i);
        }.bind(i), i*1000);
    }
}

callbackTest();
Wes Modes
  • 2,024
  • 2
  • 22
  • 40
  • Bind creates a function from another function where some of the parameters are filled in with specific values. Your function doesn't take any parameters so using bind doesn't make any sense – Aluan Haddad Apr 28 '18 at 00:24
  • Your approach doesn't need that binding, just use a IIFE to pass the variable `i` – Ele Apr 28 '18 at 00:28
  • @charlietfl, so you say. Though I was unable to find an earlier answer. Link? – Wes Modes Apr 28 '18 at 00:45
  • Huh? not sure what you are asking. If looking for the duplicate link it's at top of page as placed by the system – charlietfl Apr 28 '18 at 00:47

1 Answers1

2

bind, when provided a single parameter, sets the function's this - in your example, the i inside the function is not being bound or altered at all - it's still just using the (global) i. You should use bind's second parameter, which assigns to the function's first argument, and give your function an appropriate first argument so that it can be used:

function callbackTest() {
    for(i=0; i<3; i++){
       setTimeout(function(internalI){
            console.log("Iteration number ", internalI); 
        }.bind(null, i), i*1000);
    }
}

callbackTest();

You could also use no arguments at all and continue using bind(i), but then you would have to replace i with this inside your function, which generally isn't the sort of thing that a this should refer to:

function callbackTest() {
    for(i=0; i<3; i++){
       setTimeout(function(){
            console.log("Iteration number " + this); 
        }.bind(i), i*1000);
    }
}

callbackTest();

Or, you can just declare i with let, which has block scope, not global scope or function scope:

function callbackTest() {
  for (let i = 0; i < 3; i++) {
    setTimeout(function() {
      console.log("Iteration number ", i);
    }, i * 1000);
  }
}

callbackTest();
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Or he could use `this` inside the function instead of `i`. – Barmar Apr 28 '18 at 00:25
  • In this example, what's the point of binding ```i``` at all? – Wes Modes Apr 28 '18 at 00:27
  • Binding is totally unnecessary, instead, an IIFE passing the variable `i` and returning a function is more suitable. – Ele Apr 28 '18 at 00:28
  • Also, in my actual application, the caller passes its own param and I can't pass internalI – Wes Modes Apr 28 '18 at 00:28
  • See my edit above about a param. – Wes Modes Apr 28 '18 at 00:36
  • Any caveats to let? What is preferred? IIFE or Let? – Wes Modes Apr 28 '18 at 00:39
  • @CertainPerformance, want to provide an IIFE example? – Wes Modes Apr 28 '18 at 00:39
  • 1
    @WesModes `let` (or `const`) is *definitely* the preferable method, since it's clearest and easiest - if you need to support ancient browsers, just Babel first. (Babel is probably part of your build process already if you're developing something semi-professionally) Another good option is to avoid `for` loops entirely use an array method instead, which will give you function scope automatically. – CertainPerformance Apr 28 '18 at 00:42