Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to structure javascript callback so that function scope is maintained properly

I'm using XMLHttpRequest, and I want to access a local variable in the success callback function.

Here is the code:

function getFileContents(filePath, callbackFn) {       var xhr = new XMLHttpRequest();     xhr.onreadystatechange = function() {         if (xhr.readyState == 4) {             callbackFn(xhr.responseText);         }     }     xhr.open("GET", chrome.extension.getURL(filePath), true);     xhr.send(); } 

And I want to call it like this:

var test = "lol";  getFileContents("hello.js", function(data) {     alert(test); }); 

Here, test would be out of the scope of the callback function, since only the enclosing function's variables are accessible inside the callback function. What is the best way to pass test to the callback function so the alert(test); will display test correctly?

Edit:

Now, if I have the following code calling the function defined above:

for (var test in testers) {     getFileContents("hello.js", function(data) {         alert(test);     }); } 

The alert(test); code only prints the last value of test from the for loop. How do I make it so that it prints the value of test during the time at which the function getFileContents was called? (I would like to do this without changing getFileContents because it's a very general helper function and I don't want to make it specific by passing a specific variable like test to it.

like image 397
Chetan Avatar asked May 24 '10 22:05

Chetan


People also ask

How JavaScript callbacks are implemented?

A custom callback function can be created by using the callback keyword as the last parameter. It can then be invoked by calling the callback() function at the end of the function. The typeof operator is optionally used to check if the argument passed is actually a function. console.

How do JavaScript callback functions work?

A JavaScript callback is a function which is to be executed after another function has finished execution. A more formal definition would be - Any function that is passed as an argument to another function so that it can be executed in that other function is called as a callback function.

What is the purpose of callback implement in a program effectively?

A callback is usually used when you have to deal with a function (or method) which you don't know how much time could take to compute.

How do you pass a callback function in JavaScript?

Passing a function to another function or passing a function inside another function is known as a Callback Function. Syntax: function geekOne(z) { alert(z); } function geekTwo(a, callback) { callback(a); } prevfn(2, newfn);


2 Answers

With the code you have provided test will still be in scope inside the callback. xhr will not be, other than xhr.responseText being passed in as data.

Updated from comment:

Assuming your code looks something like this:

for (var test in testers)   getFileContents("hello"+test+".js", function(data) {     alert(test);   }); } 

As this script runs, test will be assigned the values of the keys in testers - getFileContents is called each time, which starts a request in the background. As the request finishes, it calls the callback. test is going to contain the FINAL VALUE from the loop, as that loop has already finished executing.

There is a technique you can use called a closure that will fix this sort of problem. You can create a function that returns your callback function, creating a new scope you can hold onto your variables with:

for (var test in testers) {   getFileContents("hello"+test+".js",      (function(test) { // lets create a function who has a single argument "test"       // inside this function test will refer to the functions argument       return function(data) {         // test still refers to the closure functions argument         alert(test);       };     })(test) // immediately call the closure with the current value of test   ); } 

This will basically create a new scope (along with our new function) that will "hold on" to the value of test.

Another way of writing the same sort of thing:

for (var test in testers) {   (function(test) { // lets create a function who has a single argument "test"     // inside this function test will refer to the functions argument     // not the var test from the loop above     getFileContents("hello"+test+".js", function(data) {         // test still refers to the closure functions argument         alert(test);     });   })(test); // immediately call the closure with the value of `test` from `testers` } 
like image 166
gnarf Avatar answered Sep 22 '22 18:09

gnarf


JavaScript uses lexical scoping, which basically means that your second code example will work just like how you intend it to work.

Consider the following example, borrowed from David Flanagan's Definitive Guide1:

var x = "global";  function f() {   var x = "local";   function g() { alert(x); }   g(); }  f();  // Calling this function displays "local" 

Also keep in mind that unlike C, C++ and Java, JavaScript does not have block-level scope.

In addition, you may also be interested in checking out the following article, which I highly recommend:

  • Mozilla Developer Center: Working with Closures

1 David Flanagan: JavaScript - The Definitive Guide, Fourth Edition, Page 48.

like image 29
Daniel Vassallo Avatar answered Sep 22 '22 18:09

Daniel Vassallo