0
for (var i = 0; i < json.length; i++) {
    $.Mustache.load('/mustaches.php', function(i) {
         //Do Something
    });
}

How do I pass the var i to the function in this case?

EDIT: Sorry I don't actually want to make the Mustache.load call too many times. Only once. How can I do that?

dtgee
  • 1,272
  • 2
  • 15
  • 30
  • 2
    It already has access to it - it's in the same closure. Note that since you're in a loop you're facing the infamous closure/loop problem. – Benjamin Gruenbaum Aug 16 '13 at 19:41
  • If you can assume it exists, `function(i) {…}.bind(null, i)` – Ry- Aug 16 '13 at 19:41
  • You can't "pass" `i` -- the list of arguments that get passed is up to the `$.Mustache.load` function. You can *ensure that it is in scope*, however. – apsillers Aug 16 '13 at 19:43
  • Possible duplicate of [Javascript closure inside loops - simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – apsillers Aug 16 '13 at 19:50

2 Answers2

2

This is a little more complicated than you might think, as you must ensure you pass the right value of i, so that the callback doesn't use the value of end of loop.

for (var i = 0; i < json.length; i++) {
    (function(i){
        $.Mustache.load('/mustaches.php', function() {
            // use i. Call a function if necessary
            //Do Something
        });
    })(i);
}

About the callback term : it refers to a function you pass as argument so that the function you call can call it back.

To understand the code I wrote, you must

  • understand that the callback is called later, when the loop has finished and so when i in the loop has the value of end of loop
  • that the scope of a non global variable is the function call in which it is defined. That's why there's this intermediate function : to define another variable i which is called with the value of the loop
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • Thanks! This works perfectly, but can you please explain how the `}}(i);` part works? And what exactly is a callback? Sorry I'm still quite new to javascript and stuff. – dtgee Aug 16 '13 at 20:09
  • Thank you! I don't fully understand everything still, but I get the jist of it. By the way, I also have an edit to make, I just found out I don't want to call the Mustache load too many times. Just once. How can I do that now? – dtgee Aug 16 '13 at 20:33
  • You want to call it once ? Why do you call it in a loop, then ? – Denys Séguret Aug 16 '13 at 20:48
  • Well there are two things going on. One is the mustaches.php load. The other is the stuff inside the function. I don't know how to separate the two, though. – dtgee Aug 16 '13 at 21:40
0

An elegant way to solve your question would be using the bind method.

for (var i = 0; i < json.length; i++) {
    $.Mustache.load('/mustaches.php', function(i) {
         //Do Something
    }.bind(this, i));
}

the bind method returns a new function with a new context (in this case this) and applies one (or more) argument(s) to your function (i in this particular case). You can find more about bind and currying here.

EDIT. You can optimise your loop by loading the template only once. In fact, $.Mustache.load fetches /mustache.php on each cycle of the loop. Also, because the function asynchronously fetches the template with AJAX, you might get not consistent ordering in your template (one response may take longer than others). The fix is pretty straightforward: we load the template and then we iterate through the loop.

$.get('/mustache.php').done(function(template){
  $.Mustache.add('my-template', template);
  for (var i = 0, len = json.length; i < len; ++i) {
    var rendered_template = $.Mustache.render('my-template', {
      i: i,
      ...
    });
  }
});
danielepolencic
  • 4,905
  • 1
  • 26
  • 25
  • Nice! I'll take a look at it. Could you also see my edit please? – dtgee Aug 16 '13 at 20:34
  • [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) is elegant but isn't compatible with IE8. – Denys Séguret Aug 16 '13 at 20:48
  • @dystroy there is a [polyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) if you plan to support IE8 – danielepolencic Aug 16 '13 at 20:52
  • What do I do with rendered_template? And is the `...` just variables I pass? – dtgee Aug 16 '13 at 21:51
  • `rendered template` contains the string with the rendered template. hence if template is `Hello {{i}}`, when `i=1` then `rendered_template='Hello 1'`. As for the `...`, those are additional variable you can pass to your template. You can find more about `render` and jQuery Mustache [here](https://github.com/jonnyreeves/jquery-Mustache) – danielepolencic Aug 17 '13 at 09:57