13

I've been wondering, is there a better way to pass variables into callback functions in node.js other than using bind().

Here is an example:

var fs = require('fs');

for(var i = 0; i < 100; i++) {
    fs.writeFile(i + ".txt", i, function(error) {
        fs.stat(this.i + ".txt", function() {
            fs.rename(this.i + ".txt", this.i + ".new.txt", function() {
               console.log("[" + this.i + "] Done...");
            }.bind({ i: this.i }));
        }.bind({ i: this.i }));
    }.bind({ i: i }));
}

Notice the bind() methods all the way up, simply passing the value of i.

Thanks.

2
  • If I am not wrong, You can use iteration (i) values inside calbacks without any assignments. Commented Sep 28, 2012 at 6:28
  • I believe you are wrong. If you run this without the bind() methods, the console.log statement says [ undefined ] done... Commented Sep 28, 2012 at 6:35

4 Answers 4

15

Variables in JavaScript are valid for the whole function scope. This means that you can define a variable x ((var x = ...) and it is still accessible in all functions, you define within the same calling scope. (For detailed information you might want to take a look at JavaScript Closures

The problem of your case is, that you manipulate your i during the for loop. If simply access the i in the callback functions, you'd recieve the first value that is no longer in the loop.

You can avoid that by calling a new function with the i as argument, like this:

var fs = require('fs');

// still use your for-loop for the initial index
// but rename i to index to avoid confusion
for (var index = 0; index < 100; index++) {
  // now build a function for the scoping
  (function(i) {
    // inside this function the i will not be modified
    // as it is passed as an argument
    fs.writeFile(i + ".txt", i, function(error) {
      fs.stat(i + ".txt", function() {
        fs.rename(i + ".txt", i + ".new.txt", function() {
          console.log("[" + i + "] Done...");
        });
      });
    });
  })(index) // call it with index, that will be i inside the function
}
Sign up to request clarification or add additional context in comments.

3 Comments

How can you just throw i is as a function argument into writeFile(). Its prototype is (filename, callback) right? Also, how come fs.stat() does not pass i as a function arg as well?
I did not check your code, just transformed it to use the passed variables :D
unnecessary anonymous function will make script slow
7

I would like to do with below:

var fs = require('fs');

var getWriteFileCallback = function(index) {
  return function(error) {                           
    fs.stat(index + '.txt', function() {             
      fs.rename(index + '.txt', index + '.new.txt', function() {
        console.log("[" + index + "] Done...");      
      });                                            
    });                                              
  };                                                 
}                                                    

for(var i = 0; i < 100; i++) {
  fs.writeFile(i + ".txt", i, getWriteFileCallback(i));
}

Comments

0

You can use let instead of var in your for loop. This is (in my eyes at least) the biggest difference between the two! Just make sure you use strict mode or let won't work for you.

var fs = require('fs');

for(let i = 0; i < 100; i++) {
    fs.writeFile(i + ".txt", i, function(error) {
        fs.stat(i + ".txt", function() {
            fs.rename(i + ".txt", i + ".new.txt", function() {
               console.log("[" + i + "] Done...");
            });
        });
    });
}

Comments

0

The way I usually do it is to use .bind({vars here}) and then reference it with this.varname inside of the callback. Make sure not to use arrow function on your callback, just a plain function.

someArray = [{id:1}, {id:4}, {id:10}]

someArray.forEach(item=>{ 
    // item.id will be correct here
    someAsyncFunc({id:item.id}, function(err, data){
        // we need to use this.tempItem.id here because it could be returned sometime in the future
        console.log(`someAsyncFunc on item.id ${this.tempItem.id} returned`)
    }.bind({tempItem:item}))
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.