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.
Related
I have a JavaScript constructor function that I want to use to fetch some data asynchronously via Ajax first, and once that's done, call itself again to manipulate the fetched data. This basically means calling the same instance of the constructor function again, but I can't get it to work. Here is a skeleton of what I'm trying to do:
function ajaxmenu(file){
var filefetched = false
var instance = this // save reference to this instance of ajaxmenu
if (!filefetched){
$.get(file, function( data ){
$(data).appendTo(document.body)
filefetched = true
instance() // how can I call instance again to initialize menu again now that ajax file is loaded?
return
})
}
this.menu = $('#menuid') // uses jQuery
this.menu.css({width: '100px'})
//do something else fancy with $menu
}
var menu = new ajaxmenu('menu.htm')
So basically the logic I want to happen here is, when ajaxmenu() is instantiated, the portion inside ajaxmenu() that fetches some file via Ajax is called, then once that's done, the same instance of ajaxmenu() is called again, but this time with the file in place already for the reminder of the function to parse and manipulate.
How can I do this? What I have now, calling instance() returns an error.
Generally speaking, the answer to your question Can I call an initialized object again is yes! You have many options.
If you insist to use the same function again, then one option would be to
add a second parameter filefetched to ajaxmenu
you do not need the var instance = this;
pass a true when calling the ajaxmenu from itself, to skip the fetching: ajaxmenu(file, true);
Full code:
function ajaxmenu(file, filefetched){
if (!filefetched){
$.get(file, function( data ){
$(data).appendTo(document.body)
ajaxmenu(file, true);
});
}
this.menu = $('#menuid') // uses jQuery
this.menu.css({width: '100px'})
//do something else fancy with $menu
}
var menu = new ajaxmenu('menu.htm', false);
Another option (without a self-call) would be to use the callbacks that jQuery offers with the get function and do your change to the menu in the done event, that is executed once the get function completes. This way you don't need the recursive call:
function ajaxmenu(file){
$.get(file, function( data ){
$(data).appendTo(document.body)
})
.done(function() {
this.menu = $('#menuid') // uses jQuery
this.menu.css({width: '100px'})
//do something else fancy with $menu
});
}
var menu = new ajaxmenu('menu.htm');
This also simplifies the code a lot, because you don't need branches (and/or recursive calls) and it is much more readable and so better maintainable.
Generally on recursive calls: you always need a condition to stop the recursion to prevent an infinite loop. One possibility would be using a parameter that is change on every new recursive call:
function process(data, n)
{
// process data
// iterate again or stop recursion
if (n > 0)
{
process(data, n - 1);
}
// done => n = 0
}
// start
process(data, 5);
Another option would be using a global variable and track it's state, but this generally indicates a bad design and it is not recommended:
// global variable
var n = 5;
function process(data)
{
// process data
// iterate again or stop recursion
if (n > 0)
{
n = n - 1;
process(data);
}
// done => n = 0
}
// start
process(data);
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.
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.
I read an online book. It gave an callback pattern example as follow.
var findNodes = function () {
var i = 100000, // big, heavy loop
nodes = [], // stores the result
found; // the next node found
while (i) {
i -= 1;
// complex logic here...
nodes.push(found);
}
return nodes;
};
var hide = function (nodes) {
var i = 0, max = nodes.length;
for (; i < max; i += 1) {
nodes[i].style.display = "none";
}
};
// executing the functions
hide(findNodes());
It said that this is not efficient, for it loop through found nodes twice, and the following code is more efficient.
// refactored findNodes() to accept a callback
var findNodes = function (callback) {
var i = 100000,
nodes = [],
found;
// check if callback is callable
if (typeof callback !== "function") {
callback = false;
}
while (i) {
i -= 1;
// complex logic here...
// now callback:
if (callback) {
callback(found);
}
nodes.push(found);
}
return nodes;
};
// a callback function
var hide = function (node) {
node.style.display = "none";
};
// find the nodes and hide them as you go
findNodes(hide);
However, both of them are O(n), and the overhead of calling a function may be large, which causes each iteration in findNodes() (with callback) takes more time. So I wonder if this modification really makes different as the author said. And how should I measure the cost of the two implements?
Depending on the size of the array, the example where it's only looped once can be more efficient.
However, your concerns are correct. Especially in bit older JS engines there is significant overhead in function calls.
Like with all performance optimizations, this is something you should measure. Use a profiler to test the code to find bottlenecks, then optimize, then rerun profiling to find out if it had a positive effect.
I put the two examples in two functions in an HTML file, and used the Chrome console to time them, like this:
console.time('slow'); slow(); console.timeEnd('slow');
console.time('fast'); fast(); console.timeEnd('fast');
This shows that the first example, the "inefficient" one, runs twice as fast as the second implementation.
This question already has answers here:
Semaphore-like queue in javascript?
(4 answers)
Closed 6 years ago.
I have created a Queue class in javascript and I would like to store functions as data in a queue. That way I can build up requests (function calls) and respond to them when I need to (actually executing the function).
Is there any way to store a function as data, somewhat similar to
.setTimeout("doSomething()", 1000);
except it would be
functionQueue.enqueue(doSomething());
Where it would store doSomething() as data so when I retrieve the data from the queue, the function would be executed.
I'm guessing I would have to have doSomething() in quotes -> "doSomething()" and some how make it call the function using a string, anyone know how that could be done?
All functions are actually variables, so it's actually pretty easy to store all your functions in array (by referencing them without the ()):
// Create your functions, in a variety of manners...
// (The second method is preferable, but I show the first for reference.)
function fun1() { alert("Message 1"); };
var fun2 = function() { alert("Message 2"); };
// Create an array and append your functions to them
var funqueue = [];
funqueue.push(fun1);
funqueue.push(fun2);
// Remove and execute the first function on the queue
(funqueue.shift())();
This becomes a bit more complex if you want to pass parameters to your functions, but once you've setup the framework for doing this once it becomes easy every time thereafter. Essentially what you're going to do is create a wrapper function which, when invoked, fires off a predefined function with a particular context and parameter set:
// Function wrapping code.
// fn - reference to function.
// context - what you want "this" to be.
// params - array of parameters to pass to function.
var wrapFunction = function(fn, context, params) {
return function() {
fn.apply(context, params);
};
}
Now that we've got a utility function for wrapping, let's see how it's used to create future invocations of functions:
// Create my function to be wrapped
var sayStuff = function(str) {
alert(str);
}
// Wrap the function. Make sure that the params are an array.
var fun1 = wrapFunction(sayStuff, this, ["Hello, world!"]);
var fun2 = wrapFunction(sayStuff, this, ["Goodbye, cruel world!"]);
// Create an array and append your functions to them
var funqueue = [];
funqueue.push(fun1);
funqueue.push(fun2);
// Remove and execute all items in the array
while (funqueue.length > 0) {
(funqueue.shift())();
}
This code could be improved by allowing the wrapper to either use an array or a series of arguments (but doing so would muddle up the example I'm trying to make).
Canonical answer posted here
Here is a nice Queue class you can use without the use of timeouts:
var Queue = (function(){
function Queue() {};
Queue.prototype.running = false;
Queue.prototype.queue = [];
Queue.prototype.add_function = function(callback) {
var _this = this;
//add callback to the queue
this.queue.push(function(){
var finished = callback();
if(typeof finished === "undefined" || finished) {
// if callback returns `false`, then you have to
// call `next` somewhere in the callback
_this.next();
}
});
if(!this.running) {
// if nothing is running, then start the engines!
this.next();
}
return this; // for chaining fun!
}
Queue.prototype.next = function(){
this.running = false;
//get the first element off the queue
var shift = this.queue.shift();
if(shift) {
this.running = true;
shift();
}
}
return Queue;
})();
It can be used like so:
var queue = new Queue;
queue.add_function(function(){
//start running something
});
queue.add_function(function(){
//start running something 2
});
queue.add_function(function(){
//start running something 3
});
Refer to the function you're storing without the () at the end. doSomething is a variable (that happens to be a function); doSomething() is an instruction to execute the function.
Later on, when you're using the queue, you'll want something like (functionQueue.pop())() -- that is, execute functionQueue.pop, and then execute the return value of that call to pop.
You can also use the .call() method of a function object.
function doSomething() {
alert('doSomething');
}
var funcs = new Array();
funcs['doSomething'] = doSomething;
funcs['doSomething'].call();
In addition, you can also add the function directly to the queue:
funcs['somethingElse'] = function() {
alert('somethingElse');
};
funcs['somethingElse'].call();