Calling an Async function multiple times - javascript

I have an Async function
function AsyncFunc(args){
//does some async work here
}
then I call this function multiple times in a for loop:
for(var i=0; i<10; i++)
{
AsyncFunc(i);
}
Does this create multiple copies of the AsyncFunc? or are the local variables defined in AsyncFunc getting overridden by the subsequent calls?
EDIT
Suppose the AsyncFunc does following:
function AsyncFunc(args){
$.get(args.url, function(data){
args.data = data;
});
}
then I call this function multiple times in a for loop:
for(var i=0; i<10; i++)
{
AsyncFunc(args_object_with_a_different_url);
}
Now would data go into their corresponding args object? In other words, would the callback attach to the copy of the function in which the ajax request was initiated?

The AsynFunc() is placed on the call stack 10 times. Each call contains a localized copy of all variables and functions defined within it. Therefore, they do not share state and "get overriden".
The calls share no state with one another other than any references to objects within the global name space.
EDIT:
Example where they would potentially "share":
var mySharedVariable = 0;
function AsyncFunc(args) {
var myLocalVariable = mySharedVariable++;
console.log(myLocalVariable);
// do some asynchronous task that i'm too lazy to code for the example as it's not relevant
}
for(var i = 0; i < 10; i++)
AsyncFunc(i);
console.log(mySharedVariable);
As you can see here, if we were to output mySharedVariable at the end, it would output 10. However, if we output myLocalVariable we would see something akin to 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 since they are local variables and do not share state.
Edit:
jQuery async call example:
for (var i = 0; i < 10; i++)
$.post('someURL', function(response) {
var myLocalVariable = response.data;
//the data returned is not a copy, it's an individual instance per call invoked.
//neither is myLocalVariable shared, each invocation of this callback has it's own memory allocated to store its value
});
Edit:
To your most recent question, every data object would be unique to each callback after the get request. However, your code doesn't assure me that the args variable being passed in is different each time so there is a chance that your implementation could lead to args.data being overriden with each callback. So take a look below at another option to ensure you store all of the data objects from your callbacks appropriately.
var args = [];
function AsyncFunc(args){
$.get(args.url, function(data){
args.push(data);
});
}
//now args will have 10 uniquely different data objects stored inside.

Related

Why does moving an async callback outside of a loop change the result?

Consider the following code snippets:
for(var i = 0; i < arr.length; ++i) {
var newObject = new someFunction(arr[i]);
//async callback function
$http.get('someurl').then(
function(data) {
newObject.data = data;
}
);
}
VS
function registerCallbacks(o) {
$http.get('someurl').then(
function(data) {
o.data = data;
}
);
}
for(var i = 0; i < arr.length; ++i) {
var newObject = new someFunction(arr[i]);
registerCallbacks(newObject);
}
The first example will perform the async operation only on the last object in the array, while the second example will work as expected.
I understand that in the first example, the callbacks all refer to the same variable 'newObject', so only one object is acted upon.
But why isn't it like this in the second example as well? Wouldn't 'o' end up referring to the parameter of the last function call?
I'm afraid I've missed something fundamental about how values are passed in javascript and would be grateful if someone could elucidate me on how it works.
Cheers!
In the first example, newObject object may not have the same value by the time asynch callBack is invoked since its scope is still there in its parent method, and the prevailing values of newObject will be used by the inner block.
However, in the second one newObject is passed to another method as o (a new reference) which doesn't have the same variables in scope since it is outside that for loop block.

Correct way to pass a variable argument in a callback in JavaScript?

I feel this should be answered somewhere in the internet but I failed to find it, maybe because I'm not searching the correct terms but this is the problem: I have the following function:
function ParentFunction (DataBase, Parameters) {
for (k = 0; k < DataBase.length; k++){
var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
$.ajax({
url: CalendarURL,
dataType: 'json',
timeout: 3000,
success: function( data ) { succesFunction(data, k, Parameters);},
error: function( data ) { errorFunction ("Error",Parameters); }
});
}
}
I was getting errors in succesFunction(data, k, Parameters) because 'k' was always evaluated with the latest value. What is happening is that, when the for loop runs k is correctly increased but, when the callback function successFunction was executed, typically several ms after the loop was finished, it was always been evaluated with the last value of k, not the value of the loop the $.ajax was called.
I fixed this by creating another function that contains the ajax call. It looks like this:
function ParentFunction (DataBase, Parameters) {
for (k = 0; k < DataBase.length; k++){
var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
AjaxCall(CalendarURL, k, Parameters);
}
}
function AjaxCall(URL, GroupIndex, Parameters) {
$.ajax({
url: URL,
dataType: 'json',
timeout: 3000,
success: function( data ) { succesFunction(data, GroupIndex, Parameters);},
error: function( data ) { errorFunction ("Error",Parameters); }
});
}
and it works. I think when the function is called in the parentFunction a copy of the value of the arguments is created and when the callback executes sees this value instead of the variable k which by the time would have a wrong value.
So my question is, is this the way to implement this behaviour? Or is there more appropriate way to do it? I worry that either, different browsers will act differently and make my solution work in some situations and not work in others.
You are hitting a common problem with javascript: var variables are function-scoped, not block-scoped. I'm going to use a simpler example, that reproduces the same problem:
for(var i = 0; i < 5; i++) {
setTimeout(function() { alert(i) }, 100 * i);
}
Intuitively, you would get alerts of 0 through 4, but in reality you get 5 of 5, because the i variable is shared by the whole function, instead of just the for block.
A possible solution is to make the for block a function instead:
for(var i = 0; i < 5; i++) {
(function(local_i) {
setTimeout(function() { alert(local_i); }, 100 * i);
})(i);
}
Not the prettiest or easier to read, though. Other solution is to create a separate function entirely:
for(var i = 0; i < 5; i++) {
scheduleAlert(i);
}
function scheduleAlert(i) {
setTimeout(function() { alert(i); }, 100 * i);
}
In the (hopefully near) future, when browsers start supporting ES6, we're going to be able to use let instead of var, which has the block-scoped semantics and won't lead to this kind of confusion.
Another option – rather than creating a new named function – would be to use a partial application.
Simply put, a partial application is a function that accepts a function that takes n arguments, and m arguments that should be partially applied, and returns a function that takes (n - m) arguments.
A simple implementation of a left-side partial application would be something like this:
var partial = (function() {
var slice = Array.prototype.slice;
return function(fn) {
var args = slice.call(arguments,1);
return function() {
return fn.apply(this, args.concat(slice.call(arguments)));
}
}
}).call();
With this, then you can take a function that requires two arguments like:
function add(a,b) { return a + b; }
Into a function that requires only one argument:
var increment = partial(add, 1);
increment(1); // 2
increment(10); // 11
Or even a function that requires no arugments:
var return10 = partial(add, 5, 5);
return10(); // 10
This is a simple left-side only partial application function, however underscore.js provides a version that can partially apply an argument anywhere in the argument list.
For your example, instead of calling AjaxCall() to create a stable variable scope, you could instead do:
function ParentFunction (DataBase, Parameters) {
for (k = 0; k < DataBase.length; k++){
var CalendarURL = "https://www.google.com/calendar/feeds/" + DataBase.cid;
var onSuccess = _.partial(succesFunction, _, k, Parameters);
$.ajax({
url: CalendarURL,
dataType: 'json',
timeout: 3000,
success: onSuccess,
error: function( data ) { errorFunction ("Error",Parameters); }
});
}
}
Here, we are using _.partial() to transform a function with a signature of:
function(data, index, params) { /* work */ }
into a signature of:
function(data) { /* work */ }
Which is the signature that the success callback will actually be invoked with.
Though admittedly, this is all pretty much just syntactical sugar for the same underlying concepts already described, it can sometimes conceptually help to think about problems like these from as functional perspective than procedural one.
This has to do with closures in javascript. Your anonymous functions each reference a variable outside of their current scope, so each function's "k" is bound to the original looping variable "k." Since these functions are called some time after, each function looks back to see that "k" is sitting at its last value.
The most common way to get around this is exactly what you did. Instead of using "k" in a nested function definition (which forces a closure), you pass it as an argument to an external function, where no closure is needed.
Here are a few posts with similar issues:
How do JavaScript closures work?
JavaScript closure inside loops – simple practical example
Javascript infamous Loop issue?

Asynchronous Request Chaining

I'm looking for advice on extending a previous accepted answer regarding chained ajax requests.
The following asynchronous-chain solution was proposed to sequence 3 ajax requests:
var step_3 = function() {
c.finish();
};
var step_2 = function(c, b) {
ajax(c(b.somedata), step_3);
};
var step_1 = function(b, a) {
ajax(b(a.somedata), step_2);
};
ajax(a, step_1);
This is great for a small pre-determined number of chained ajax functions but does not scale well to the case of a variable number of such functions.
I've tried doing the following but seem to run into scoping issues due my admitted lack of javascript expertise:
var asynch = function (options, fNext) {// do something asynchronously}
var chain = {f:[]} // chain of asynchronous functions
var args = function(n){ //return arguments to feed n'th asynch function }
for (n=0;n<N;n++)
{
var a = args(n);
var ftmp = n==N-1? function(){} : chain.f[n+1]
chain.f[n] = function () {asynch(a, ftmp)}
}
chain.f[0]() // initiate asynchronous chain
What you have is a very common scoping issue with for loops. Each iteration of the for loop is using the same local scope as the parent function, meaning anything that happens asynchronously will end up accessing the last value of the loop rather than the value at the time it was defined. See this fiddle as an example: http://jsfiddle.net/GAG6Q/ Instead of asynch getting called 9 times, it gets called once with a value of 9. You can fix it by simply providing a private scope for the inside of the loop. You'll also want to wrap chain.f[n+1] in a function so that you don't try to assign undefined to ftmp.
http://jsfiddle.net/GAG6Q/1/
var N = 10;
var asynch = function (options, fNext) {
console.log(options);
setTimeout(fNext,500);
}// do something asynchronously}
var chain = {f:[]} // chain of asynchronous functions
var args = function(n){return n;} //return arguments to feed n'th asynch function }
for (n=0;n<N;n++)
{
(function(n){
var a = args(n);
var ftmp = n==N-1? function(){} : function(){chain.f[n+1]();};
chain.f[n] = function () {asynch(a, ftmp)}
})(n);
}
chain.f[0]() // initiate asynchronous chain
Asynchronous loops are a pain in the ass. As a rule of thumb, if you want to do them by hand you need to rewrite the for loop as a recursive function.
function sequence_synchronous(steps){
for(var i=0; i<steps.length; i++){
steps[i]();
}
return;
}
function sequence_async(steps, callback){
var i = 0;
var next_step = function(){
if(i >= steps.length){
callback();
}else{
steps[i](function(){
i++;
next_step();
});
}
}
next_step();
}
Note that this doesn't attempt to build a big chain of callbacks before calling the first one - all we did was convert the traditional for loop into continuation passing style.
I would highly recommend looking for a library to do this for you though.

Understanding closures: Constructing a meta-function that queues functions together

In terms of solving the problem, I have a fully working solution that I just finished here:
// synchronous dynamic script loading.
// takes an array of js url's to be loaded in that specific order.
// assembles an array of functions that are referenced more directly rather than
// using only nested closures. I couldn't get it going with the closures and gave up on it.
function js_load(resources, cb_done) {
var cb_list = []; // this is not space optimal but nobody gives a damn
array_each(resources, function(r, i) {
cb_list[i] = function() {
var x = document.body.appendChild(document.createElement('script'));
x.src = r;
console.log("loading "+r);
x.onload = function() {
console.log("js_load: loaded "+r);
if (i === resources.length-1) {
cb_done();
} else {
cb_list[i+1]();
}
};
};
});
cb_list[0]();
}
I am completely happy with this because it does what I want now, and is probably far easier to debug than what my first approach, if it had succeeded, would have been.
But what i can't get over is why I could never get it to work.
It looked something like this.
function js_load(resources, cb_done) {
var cur_cont = cb_done;
// So this is an iterative approach that makes a nested "function stack" where
// the inner functions are hidden inside the closures.
array_each_reverse(resources, function(r) {
// the stack of callbacks must be assembled in reverse order
var tmp_f = function() {
var x = document.body.appendChild(document.createElement('script'));
x.src = r;
console.log("loading "+r);
x.onload = function() { console.log("js_load: loaded "+r); cur_cont(); }; // TODO: get rid of this function creation once we know it works right
};
cur_cont = tmp_f; // Trying here to not make the function recursive. We're generating a closure with it inside. Doesn't seem to have worked :(
});
cur_cont();
}
It kept trying to call itself in an infinite loop, among other strange things, and it's really hard to identify which function a function is and what a function contains within it, during debugging.
I did not dig into the code, but it appears that jQuery.queue has also implemented a similar mechanism to my working one (using an array to track the queue of continuations) rather than using only closures.
My question is this: Is it possible to build a Javascript function that can take a function as argument, and enhance it with a list of other functions, by building closures that wrap functions it creates itself?
This is really hard to describe. But I'm sure somebody has a proper theory-backed mathematical term for it.
P.S. Referenced by the code above are these routines
// iterates through array (which as you know is a hash), via a for loop over integers
// f receives args (value, index)
function array_each(arr, f) {
var l = arr.length; // will die if you modify the array in the loop function. BEWARE
for (var i=0; i<l; ++i) {
f(arr[i], i);
}
}
function array_each_reverse(arr, f) {
var l = arr.length; // will die if you modify the array in the loop function. BEWARE
for (var i=l-1; i>=0; --i) {
f(arr[i], i);
}
}
The problem is how you were setting the value of cur_cont for every new function you made, and calling cur_cont in the onload callback. When you make a closure like tmp_f, any free variables like cur_cont are not 'frozen' to their current values. If cur_cont is changed at all, any reference to it from within tmp_f will refer to the new, updated value. As you are constantly changing cur_cont to be the new tmp_f function you have just made, the reference to the other functions are lost. Then, when cur_cont is executed and finishes, cur_cont is called again. This is exactly the same function that had just finished executing - hence the infinite loop!
In this sort of situation, where you need to keep the value of a free variable inside a closure, the easiest thing to do is to make a new function and call that with the value you want to keep. By calling this new function, a new variable is created just for that run, which will keep the value you need.
function js_load(resources, cb_done) {
var cur_cont = cb_done;
array_each_reverse(resources, function(r) {
// the stack of callbacks must be assembled in reverse order
// Make a new function, and pass the current value of the `cur_cont`
// variable to it, so we have the correct value in later executions.
// Within this function, use `done` instead of `cur_cont`;
cur_cont = (function(done) {
// Make a new function that calls `done` when it is finished, and return it.
// This function will become the new `cur_cont`.
return function() {
var x = document.body.appendChild(document.createElement('script'));
x.src = r;
console.log("loading "+r);
x.onload = function() {
console.log("js_load: loaded "+r);
done();
};
};
})(cur_cont);
});
// Start executing the function chain
cur_cont();
}
EDIT: Actually, this can be made even simpler by using the Array.reduce function. Conceptually, you are taking an array and producing a single function from that array, and each successive function generated should be dependant upon the last function generated. This is the problem that reduce was designed to help solve:
function js_load(resources, done) {
var queue = resources.reduceRight(function(done, r) {
return function() {
var x = document.body.appendChild(document.createElement('script'));
x.src = r;
console.log("loading "+r);
x.onload = function() {
console.log("js_load: loaded "+r);
done();
};
};
}, done);
queue();
};
Note that reduce and reduceRight are not available for older browsers (<= IE8). A JavaScript implementation can be found on the MDN page.

Functionscope and Callbacks in Node.js

so i have to calculate some share in a loop. In every iteration of that loop i have to get a variable called rent from an array. So i devided the calculate function from the database stuff.
var calculate = function() {
while(count < 100) {
var share = 50;
var shareArray = [];
for(var i = 0; i < 100; i++) {
var pension = share*2; // mathematical stuff
// Gets a rent from a database and returns it in a callback
getRent(modules, share, function(rent) {
share = rent*foo; // some fancy mathematical stuff going on here
// I need to get the share variable above out of its function scope
});
// I need the share variable right here
shareArray.push(share); // the value of share will be for i = 0: 50, i= 1: 50 ...
// This is not what i want, i need the share value from getRent()
}
count++;
}
}
Now as you may see i am presented with the following trouble. Because I'm working in node.js, the only way to get the rent variable from the modules array is through this callback function called getRent(). The thing is, i need the share value after this step but outside of getRent().
Is there any way i can do this?
This is the getRent() - Function:
var getRent = function(modules, share, callback) {
// Searching for a fitting rent in the modules array
// Just assume this is happening here
callback(rent);
};
So the question is: How can i "return" share:
getRent(modules, share, function(rent) {
share = rent*foo; // some fancy mathematical stuff going on here
// I need to get the share variable above out of its function scope
});
in any way?
If getRent is async there's no way to get the result synchronously. Fundamentally you don't know the value that getRent will end up supplying to its callback until it finally returns it. So it isn't a question of function scope, its a question of timing. You just have to wait for getRent to do its thing before you can get the value for rent. You need to refactor your code so that calculate is also async.
Something like:
// Refactor calculate to be async:
function calculate(cb) {
var data = [];
for ( var i=0; i<100; i++ ) {
getRent(function (rent) {
data.push(rent);
if ( data.length === 100 ) cb(data);
});
}
}
// And then use it async:
calculate(function (data) {
// data array arrives here with 100 elements
});
The above answer is perhaps similar to how you might achieve it with vanilla JS. Using the async library like miggs suggests is probably a good idea in the long run. But like I said, if you use vanilla JS, or the async lib, there's no getting away from the fact that you'll have to refactor towards asynchronicity both in this code and the code that calls it.
You want to use the whilst method of the async library (npm install async) to simplify this:
var count = 0;
var shareArray = [];
async.whilst(
function () {
return count < 100;
},
function (next) {
count++;
getRent(function(rent) {
// What does modules do anyway??
// Dont know where foo comes from...
shareArray.push(rent*foo); // some fancy mathematical stuff going on here
next();
});
},
function (err) {
console.log(shareArray);
// Do sth. with shareArray
}
);
If it was OK for you to request all 100 call in parallel, you could also use the parallel function.

Categories

Resources