jiajianrong jiajianrong - 2 months ago 8
Node.js Question

How to bind more parameters in javascript function

for ( var i = 0; i < files.length; i++ ) {
fs.readFile( files[i], function( file, error, data ) {
console.log("Successfully read a file", file)
}.bind(null, files[i]));
}


I want to use
Node.js
to read all files under a certain dir.
In the callback loop, files[i] does not exist due to javascript closure.
So I bind it but it appears as the first parameter.
I wonder how to make it as the last parameter, thanks.

Answer

This is an interesting question, as it's something a lot of javascript programmers make a mistake with. @torazaburo even said why not remove the pointless bind, well it's not pointless because fs.readFile is asynchronous.

I've created a little snippet below to show different ideas, there might be more.

1st is do nothing and don't bind, and is the only option that @torazaburo mentions that fails.

2nd test, is like the OP's where he has binded to the first parameter.

3rd test is binding the 'this', may as well as it's only going to be window object otherwise.. :)

4th test, use a function closure.

5th test, use the 'let', as this will capture the local scope not just the functional scope. This of course requires es6, babel etc.

6th test, use a sort of proxy function to alter the order of the parameters, this is the closesest option I can think for the OP..

var files = ['file1','file2','file3'];

function readFile(fn, callback) {
  setTimeout(function () {
    callback(null, 'filedata for ' + fn);
  },1);
}

//test 1 do nothing
function test1() {
  console.log('test1, dont use bind');
  for (var i = 0; i < files.length; i++ ) {
    readFile( files[i], function( file, error, data ) {
       console.log("Successfully read a file " + files[i])
    });
  }
}

//test 2 bind the first parameter
function test2() {
  console.log('test2, bind the first paramenter');
  for ( var i = 0; i < files.length; i++ ) {
    readFile( files[i], function( file, error, data ) {
      console.log("Successfully read a file " + file)
    }.bind(null, files[i]));
  }
}

//bind 'this', it only going to be the window object anyway, so why not use it
function test3() {
  console.log('test3, bind the this');
  for ( var i = 0; i < files.length; i++ ) {
    readFile( files[i], function( error, data ) {
      console.log("Successfully read a file " + this)
    }.bind(files[i]));
  }  
}

//use a function closure, luckly forEach is going to do that for us
function test4() {
  console.log('test4, use a closure');
  files.forEach(function (file) {
    readFile(file, function( error, data ) {
      console.log("Successfully read a file " + file)
    });
  });
}

//use let
function test5() {
  console.log('test5, use let instead of var');
  for (let i = 0; i < files.length; i++ ) {
    readFile( files[i], function( error, data ) {
       console.log("Successfully read a file " + files[i])
    });
  }
}

//create proxy function
function proxy_readFile(fn, cb) {
  return readFile(fn, function (error, data) { 
    cb(error, data, fn) });
}

function test6() {
  console.log('test6, use a proxy function');
  for ( var i = 0; i < files.length; i++ ) {
    proxy_readFile( files[i], function(error, data, file) {
      console.log("Successfully read a file " + file)
    }); 
  }
}



setTimeout(test1,100);
setTimeout(test2,200);
setTimeout(test3,300);
setTimeout(test4,400);
setTimeout(test5,500); 
setTimeout(test6,600);