This weekend, I ran into a peculiar problem of handling race conditions in Javascript.
Here is the code that was giving me problem:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
//AJAX call
makeAjaxCall(requestObj, function(data) {
//Callback for the ajax call
//PROBLEM : HOW CAN I ACCESS VALUE OF 'i' here for the iteration for which the AJAX call was made!!!
}
}
}
Accessing the value of 'i' inside the AJAX callback would not give me the expected value because by the time AJAX call response comes back, the 'for' loop would have crossed many more iterations.
To handle this, I used the following approach:
function myFunction(requestObj, myArray) {
var i = 0;
function outerFunction() {
makeAjaxCall(requestObj, innerFunction);
}
function innerFunction(data) {
i++;
if(i<myArray.length) {
outerFunction();
}
}
outerFunction();
}
Is this the correct approach? Any other way I can improve this assuming it is a 3rd pary AJAX library call which I can't modify.
You just need to use a closure:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
//AJAX call closed over i
(function(i) { // wrap your call in an anonymous function
makeAjaxCall(requestObj, function(data) {
// i is what you think it is
}
})(i) // pass i to the anonymous function and invoke immediately
}
}
The issue is that the callbacks you're passing to the ajax call have an enduring reference to i, not a copy of its value when they were created.
Your approach is fine except that it waits to make the second ajax call until the first finishes, then waits for the second to finish before the third, etc. Unless you have to do that (and I get the impression you don't), it's better to let them overlap.
A couple of options:
Use a builder function:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
//AJAX call
makeAjaxCall(requestObj, buildHandler(i));
}
function buildHandler(index) {
return function(data) {
// Use `index` here
};
}
}
Now, the handler has a reference to index, which doesn't change, rather than i, which does.
Use Function#bind:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
//AJAX call
makeAjaxCall(requestObj, function(index, data) {
// Use index here
}.bind(null, i));
}
}
Function#bind creates a function that, when called, will call the original function with a specific this value (we're not using that above) and any arguments you pass bind — followed by any arguments given to the bound function.
I prefer #1: It's clear to read and doesn't create a bunch of unnecessary functions (whereas in theory, #2 creates two functions per loop rather than just one).
There are two other ways of doing this: using bind, using a closure
Using bind:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
makeAjaxCall(requestObj, function(idx,data) {
//idx will be correct value
}.bind(null,i));
}
}
Using a closure:
function myFunction(requestObj, myArray) {
for(var i=0;i<myArray.length;i++) {
(function(idx){
makeAjaxCall(requestObj, function(data) {
//idx will be correct value
});
})(i);
}
}
There is also third method, use another function to create your callback
function myFunction(requestObj, myArray) {
function makeCB(idx){
return function(){
//do stuff here
}
}
for(var i=0;i<myArray.length;i++) {
makeAjaxCall(requestObj, makeCB(i));
}
}
Related
I'm trying to use the following code in Javascript. I'd like to pass a function rulefunc() a number of times into the onChange() function iteratively. I want to be able to access i from within the function when it is called. How can I do this?
var gui = new DAT.GUI();
for for (var i=0; i<5; i++) {
// want to associate ruleFunc with i
gui.add(lsys, 'grammarString').onChange(ruleFunc);
}
function ruleFunc(newVal) {
...
// access i here
}
At the event side:
Here since for loop is synchronous an IIFE is used so that right value of i is passed
IIFE and the onchange event makes a closure which makes the right value of i to be passed
at the argument
At the event callback side
Closure is used so that the function that is returned can access the value of the argument
var gui = new DAT.GUI();
for (var i=0; i<5; i++) {
// want to associate ruleFunc with i
(function(a){ //making an IIFE to make sure right value of i is passed to the function
f1.add(lsys, 'grammarString').onChange(ruleFunc(a));
})(i);
}
function ruleFunc(newVal) {
return function(){
//making a closure which will have access to the argument passed to the outer function
console.log(newVal);
}
}
You can write a function that returns a function like
function ruleFunc(i) {
return function(newVal){
// ... here use your newVal and i
}
}
And use like
f1.add(lsys, 'grammarString').onChange(ruleFunc(i));
You call the function that gets the i from the outer scope and then the returned function gets the newVal of the 'onChange' event. Note that I am calling the function ruleFunc and pass a parameter i. Inside the function you can now use your i variable and newVal.
Example of how it works. Here I add functions to the array with approprite i values. After that when I execute each function, it properly knows what i was used when it have been created. It is called closure.
var functions = [];
function ruleFunc(newVal) {
return function (){
console.log(newVal);
};
}
for(var i = 0; i < 5; i++) {
functions.push(ruleFunc(i));
}
for(var i = 0; i < functions.length; i++) {
functions[i]();
}
I have a JavaScript function like the following.
function changeTheDom(var1, var2, var3) {
// Use DWR to get some server information
// In the DWR callback, add a element to DOM
}
This function is called in a couple of places in the page. Sometimes, in a loop. It's important that the elements be added to the DOM in the order that the changeTheDom function is called.
I originally tried adding DWREngine.setAsync(false); to the beginning of my function and DWREngine.setAsync(true); to the end of my function. While this worked, it was causing utter craziness on the rest of the page.
So I am wondering if there is a way to lock the changeTheDom function. I found this post but I couldn't really follow the else loop or how the lockingFunction was intended to be called.
Any help understanding that post or just making a locking procedure would be appreciated.
Don't try to lock anything. The cleanest way is always to adapt to the asynchronous nature of your code. So if you have an asynchronous function, use a callback. In your particular case I would suggest that you split your function up in one part that is executed before the asych call and one part that is executed afterwards:
function changeTheDomBefore(var1, var2, var3) {
//some code
//...
asyncFunction(function(result){
//this will be executed when the asynchronous function is done
changeTheDomAfter(var1, var2, var2, result);
});
}
function changeTheDomAfter(var1, var2, var3, asynchResult) {
//more code
//...
}
asyncFunction is the asynchronous function which, in this example, takes one argument - the callback function, which then calls your second changeTheDom function.
I think I finally got what you mean and I decided to create another answer, which is hopefully more helpful.
To preserve order when dealing with multiple asynchronous function calls, you could write a simple Queue class:
function Queue(){
var queue = [];
this.add = function(func, data) {
queue.push({func:func,data:data});
if (queue.length === 1) {
go();
}
};
function go() {
if (queue.length > 0) {
var func = queue[0].func,
data = queue[0].data;
//example of an async call with callback
async(function() {
func.apply(this, arguments);
queue.shift();
go();
});
}
}
};
var queue = new Queue();
function doit(data){
queue.add(function(result){
console.log(result);
}, data);
}
for (var i = 0; i < 10; i++) {
doit({
json: JSON.stringify({
index: i
}),
delay: 1 - i / 10.0
});
}
FIDDLE
So everytime you invoke your async function, you call queue.add() which adds your function in the queue and ensures that it will only execute when everything else in the queue is finished.
I am trying to undrstand the code
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
from here http://bonsaiden.github.com/JavaScript-Garden/#function.closures
I understood this method :
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
Can anyone please help me by explaining the first one?
I will try to explain how I understands the first one,
first i is 0,
setTimeout is called,
self calling function "function(e)" is called with i=0,
Im stuck!! what happens when this function returns a function?
All the first one does is return a function that will be called after the timeout happens.
The purpose of it is to create a sub-scope for each iteration of the for loop so that the incrementing i isn't overridden with each iteration.
More explanation:
Lets take this apart into two different pieces:
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
This is the first piece:
for(var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i); //9-9
},1000);
}
Now, when you run this loop, you will always get console.log()'s that contain 9 instead of 0 to 9. This is because each setTimeout is using the same reference to i.
If you wrap the setTimeout part of that in an anonymous function, it creates a scope for each iteration allowing each setTimeout to have it's own i value.
for(var i = 0; i < 10; i++) {
setTimeout((function(i) {
return function() {
console.log(i); // 0-9
}
})(i), 1000)
}
The outer function inside the setTimeout gets executed immediately with an i of 0 for first iteration, 1 for second, etc. That function then in turn returns a function which is the function that setTimeout uses. A function is being generated and returned for each iteration of the loop using a different value for i.
Both end up with the same result: a setTimeout is called with a function to invoke, which writes a number from 0 to 9 on the console. Both use nested functions to get the current value of i into a closure so you don't end up logging 10 9's.
The first code chooses to have a function returning the function that setTimeout will call. The second changes the nesting order so that the closed-over function invokes setTimeout itself. The net effect is the same.
Other than stylistic reasons and personal choice, I don't see a reason to choose one over the other.
"Can you please check the updated question specifying where im getting confused"
OK, here's the long explanation. Remember that the first parameter to setTimeout() needs to be a reference to the function that you want executed after the specified delay. The simplest case is to just name a function defined elsewhere:
function someFunc() {
console.log("In someFunc");
}
setTimeout(someFunc, 100);
Note there are no parentheses on someFunc when passing it as a parameter to setTimeout because a reference to the function itself is required. Contrast with:
setTimeout(someFunc(), 100); // won't work for someFunc() as defined above
With parenthese it calls someFunc() and passes its return value to setTimeout. But my definition of someFunc() above doesn't explictly return a value, so it implicitly returns undefined - which is like saying setTimeout(undefined, 100).
But it would work if changed someFunc() to return a function instead of returning undefined:
function someFunc() {
return function() {
console.log("In the function returned from someFunc");
};
}
So now (at last) we come to the code from your question:
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
Instead of referencing a function by name and calling it as someFunc(i) it defines an anonymous function and calls it immediately as (function(e) {})(i). That anonymous function returns another function and it is that returned function that becomes the actual parameter to setTimeout(). When the time is up it is that returned function that will be executed. Because the (inner) function being returned is defined in the scope of the (outer) anonymous function it has access to the e parameter.
I made code like this, to easier connecting callbacks on events:
dojo.ready(function() {
for(var action in page.actions) {
for(var key in page.actions[action]) {
(function() {
dojo.query(key).connect(action, function(evt) {
if(page.actions[action][key]() == false)
dojo.stopEvent(evt);
});
})();
}
}
});
page = {
actions :
{
onclick :
{
"#page-action-one" : function()
{
alert("Action 1");
return false;
},
"#page-action-two" : function()
{
alert("Action 2");
return false;
}
}
}
};
But click on "#page-action-one" an "#page-action-two" make the same alert("Action 2"). I tried to use cloer, but without effect. I now, I can make it different way, but I would like to now, why is this happening.
You're trying to fix the closure issue by wrapping your event handler assignment in an anonymous function. But the key to that trick is that you have to pass in the looping variable (or variables) as an argument to the anonymous function - otherwise the anonymous function wrapper does nothing. Try:
dojo.ready(function() {
for(var action in page.actions) {
for(var key in page.actions[action]) {
(function(action, key) {
dojo.query(key).connect(action, function(evt) {
if(page.actions[action][key]() == false)
dojo.stopEvent(evt);
});
})(action, key);
}
}
});
This "fixes" the value of action and key at the time the anonymous function is called, so within the anonymous function those variable names only apply to the passed arguments, not to the named variables in the outer scope, which will update on the next loop iteration.
This question in summary is to figure out how to pass variables between javascript functions without: returning variables, passing parameters between primary functions, using global variables, and forcing function 1 to wait for function 2 to finish. I figured out a jQuery solution and posted in below (in the answers section).
Old Post: I initialize a set of four functions, each calling on each other in a different way. At the end of it, I need the final modified product (an array) returned to the initializing function.
Global variables don't force the initial function to wait. And returning it backwards four times doesn't work either. How do you pass a modified variable back to its initializing function, if you can't return it? Or why isn't it returning?
(the maze starts at initFunctionA, ends at functionD)
classOne = {
initFunctionA : function() {
classTwo.functionB(functionD, array);
// I NEED ACCESS TO ARRAY2 HERE
},
functionD : function(data, array) {
var array2 = // modifications to array
}
}
{...}
classTwo = {
functionB : function(callback, array) {
$.ajax({
success: function(ret){
classTwo.functionC(ret, callback, array)
}
});
},
functionC : function(ret, callback, array) {
callback(ret.data.x, array);
}
}
Change your callback (at the call site) such that you capture the return value of functionD. Then, change functionD so that it returns array2. I've added this access to the example below as a convenience. (Also, be sure to include semicolons where "required" if you want to make JSLint happy.)
classOne = {
initFunctionA : function() {
var self = this;
classTwo.functionB(function() {
var array2 = functionD.apply(self, arguments);
// ACCESS ARRAY2 HERE
}, array);
},
functionD : function(data, array) {
var array2 = // modifications to array
return array2;
}
};
{...}
classTwo = {
functionB : function(callback, array) {
$.ajax({
success: function(ret){
classTwo.functionC(ret, callback, array)
}
});
},
functionC : function(ret, callback, array) {
callback(ret.data.x, array);
}
};
You can't make it work with a pattern like you've written there; it's simply not possible in Javascript because there's no such thing as "waiting". Your ajax code has to take a callback parameter (which you've got, though it's not clear where it comes from or what it does), and that initial function should pass in code to do what you need with the array after the ajax call finishes.
I would use an object constructor:
function ClassOne() {
this.array2 = [];
}
ClassOne.prototype.initFunctionA = function() {
// ...
}
ClassOne.prototype.functionD = function(data, array) {
// Can use array2 EX: this.array2
}
var classOne = new ClassOne();
This is how I understand your problem:
classTwo handles an AJAX call and may modify the result. classOne makes use of classTwo to get some data and needs the resulting data.
If so, how's this:
classOne = {
initFunctionA : function() {
var array = ['a','b','c'];
classTwo.functionB(this.functionD, array);
},
functionD : function(data, array) {
// This function is called when the AJAX returns.
var array2 = // modifications to array
}
}
{...}
classTwo = {
functionB : function(callback, array) {
$.ajax({
success: function(ret){
classTwo.functionC(ret, callback, array)
}
});
},
functionC : function(ret, callback, array) {
callback(ret.data.x, array);
}
}
So classOne.initFunctionA calls classTwo.functionB which sets up an ajax call. When the ajax call completes successfully, classTwo.functionC is called with the result and the initial array. From here, classOne.functionD is called with ret.data.x and the array.
Okay! I found a way to pass variables between functions without:
making global variables
making object properties (Chaos's solution)
passing parameters
These three were suggested here as the only ways.
Accessing variables from other functions without using global variables
But, if you you can't pass parameters directly, and you need one function to wait for the other (i.e, can't rely on references), and you're using asynchronous calls to the server in an intermediate function, then your only solution is:
Using jQuery...
Create this object in the DOM (dynamically if you don't want to muddy your markup):
<div id='#domJSHandler" style="display: none;"></div>
Then in the function that must wait:
//Function & Class Set 2
$('#domJSHandler').bind('proceedWithAction', function(event, param1, param2) {
// action set 2
});
And in the function to be waited on:
//Function & Class Set 1
// action set 1
$('#domJSHandler').triggerHandler('proceedWithAction', [param1, param2]);
Essentially encase the last actions you need to perform in a jQuery bind custom event on an invisible DOM object. Trigger that event from JS with jQuery's triggerHandler. Pass your parameters and voila!
I'm sure SO will give me crap for this (and for narcissistically accepting my own answer) but I think it's pretty brilliant for a uber-newbie and it worked for me.
So :p Stack Overflow
(jk You've all saved my ass many times and I love you all :)