7

Im working with the gm npm module that deals with image manipulation. and i have this code.

for(i=0;i < 4;i++){
    gm("www/img/" + image[i]).crop(550, 406, 0, 0).write(function(err) {
         console.log(this.outname + " created  ::  " + arguments[3]); //success
    });
}

this loop is meant to loop through the images array and crop each photo, but it only crops the last one. i think its something to do function invocation and callbacks, but not advanced yet for that level.

Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
Unknown
  • 257
  • 1
  • 3
  • 10

2 Answers2

7

Change your code to:

for (var i = 0; i < 4; i++) {
  (function (i) {
    gm("www/img/" + image[i]).crop(550, 406, 0, 0).write(function(err) {
         console.log(this.outname + " created  ::  " + arguments[3]); //success
    });
  }).call(this, i);
}

otherwise the value of i will be 3 each time your callback is being invoked.

Minko Gechev
  • 24,430
  • 7
  • 59
  • 67
5

You need to create a "closure" over the variable

Js has a function scope.

for (i = 0; i < 4; i++)
{
    (function (a)
    {
        gm("www/img/" + image[a]).crop(550, 406, 0, 0).write(function (err)
        {
            console.log(this.outname + " created  ::  " + arguments[3]); //success
        });
    }).call(this,i)
}

or

that=this;

for (i = 0; i < 4; i++)
    {
        (function (a)
        {
            gm("www/img/" + image[a]).crop(550, 406, 0, 0).write(function (err)
            {
                console.log(that.outname + " created  ::  " + arguments[3]); //success
            });
        })(i)
    }

edit :

Also - I would also keep a reference to the arguments since now , after IIFE - the arguments is changing.

you can keep your arguments via :

var args= Array.prototype.slice.apply(arguments)

example :

function g()

{
for (i = 0; i < 4; i++)
    {
        (function (a)
        {
           console.log(arguments); //huh ? arguments are not a,b,c !!! anymore
        })(i);
    }
}


g('a','b','c') // 0,1,2,3

so you do need to keep reference to the arguments cuz their changed after IIFE.

Royi Namir
  • 138,711
  • 129
  • 435
  • 755
  • +1 Nice answer..Every JS beginner definitely comes across similar problem. I did too few days back :) – Sachin Mar 16 '14 at 14:20
  • @blunderboy tnx - most important is to understand that JS has function scope. and that closure is merely a scope where inner function has access to it's parent scope' . – Royi Namir Mar 16 '14 at 14:21
  • Exactly and you made the answer to the point. – Sachin Mar 16 '14 at 14:22
  • One thing you have missed is the context of `this` in the anonymous function. You should use `var that = this` and use that inside the function or just use `call` or may be use `bind` method. – Sachin Mar 16 '14 at 14:23
  • @blunderboy `this` in IIFE is the global window. if you want to keep the outer IIFE scope for future using `this` then - `than` is the right way. – Royi Namir Mar 16 '14 at 14:24
  • Exactly it is global window but looks like OP have a different context here of `this`. – Sachin Mar 16 '14 at 14:25
  • @blunderboy ooop's didnt see `this`... editing – Royi Namir Mar 16 '14 at 14:26
  • @blunderboy I believe arguments should also be handled. see below – Royi Namir Mar 16 '14 at 14:32
  • Wow!! You made an excellent catch. I can't upvote twice man :( – Sachin Mar 16 '14 at 14:36
  • Mods will throw both of us out for this off-topic conversation..Just saying :P – Sachin Mar 16 '14 at 14:37
  • `Js has a function scope.` Might be worth pointing out, modern JS has block scope too, with `let / const`.. – Keith Jul 10 '18 at 11:43