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.
Related
I'm trying to create a live demo of a backtracking algorithm (with simple forward checking) in javascript. I've gotten the algorithm down pat in its recursive form, but now I'm stuck trying to animate it using javascript's setTimeout or setInterval, which I'm assuming would require me to convert the recursive solution to an iterative one. Here's the function (rewritten to be a little more general):
function solve(model) {
if (model.isSolved()) return true;
var chosen = chooseVariable(model); //could be random or least constrained
var domain = model.getDomain(chosen);
var i, assn;
for (i = 0; i < domain.length; i++) {
assn = domain[i];
model.set(chosen, assn);
if (solve(model)) return true;
else model.undo();
}
return false;
}
As you can see, I've made it so that the model can undo it's own actions, rather than having a separate action stack or cloning the model at each level of recursion. Is there a way to convert the function above into one that could be used with setTimeout or setInterval? Would I have to significantly change the model/add another stack to keep track of the chosen variable/attempted assignments? Do I need a closure with mutating variables? I'm mostly looking for pseudocode to point me in the right direction.
I'm assuming this require me to convert the recursive solution to an iterative one.
No, right the other way round. Yours still is iterative in some parts (the for-loop).
You will have to make the steps asynchronous, so that each step takes a callback which is fired when its animation is done and you can continue. Since you will want to animate every single iteration step, you will have to make them asynchronous with a recursive-like callback - continuation passing style.
Here's how:
function solve(model, callback) {
if (model.isSolved())
return callback(true);
var chosen = chooseVariable(model); // could be random or least constrained
var domain = model.getDomain(chosen);
var i = 0, assn;
(function nextStep() {
if (i < domain.length) {
assn = domain[i];
model.set(chosen, assn);
solve(model, function(solved) {
if (solved)
callback(true);
else {
model.undo();
i++;
nextStep();
}
});
} else
callback(false);
})();
}
Now you can simply make this recursive variant asynchronous by introducing setTimeout where you need it (usually after displaying the model state):
function solve(model, callback) {
if (model.isSolved())
return callback(true);
var chosen = chooseVariable(model); // could be random or least constrained
var domain = model.getDomain(chosen);
var i = 0, assn;
(function nextStep() {
if (i < domain.length) {
assn = domain[i];
model.set(chosen, assn);
solve(model, function(solved) {
if (solved)
callback(true);
else {
model.undo();
i++;
setTimeout(nextStep, 100);
}
});
} else
setTimeout(callback, 100, false);
})();
}
You could program it asynchronously using for example deferreds. jQuery provides an implementation of deferreds and you could have a look at this example which uses timeouts:
http://api.jquery.com/deferred.promise/#example-0
Of course you need only one timeout which always resolves (succeeds).
I have the following situation (see also jsFiddle -> http://jsfiddle.net/sMuWK/):
function CallBackStringHandler() {
this.callback = function(){return null};
};
CallBackStringHandler.prototype.doTheMagic = function(callback) {
var result = callback.call(this);
if(result == null)
alert("Nothing to handle yet...");
else
alert("End the result is: \n\n" + result);
};
function Action(){
var result = null;
var max = 10;
var index = 0;
var processor = setInterval(function(){
if(index <= max){ //Processing step
if(result == null)
result = "" + index;
else
result += index;
index++;
} else { //Done
clearInterval(processor);
alert(result);
}
},10);
return result;
};
function Run(){
var handler = new CallBackStringHandler();
handler.doTheMagic(Action);
};
Run();
A script (a jQuery plugin) allows you to specify a callback that has to return a string.
This string will be handled by this script.
So far so good.
For the sake of performance and keeping my page responsive, I want to build this string in a multi-threaded way. Since this is not a web standard yet, I simulate this with the help of setInterval.
Now I know that the essence of doing things this way is not waiting for the results.
But I can't think of a way of keeping things responsive and fast and return the full result to the handler.
So the end result (in this example) should show: 012345678910.
Any help/clues would be appreciated.
Cheers, another nerd.
You need to turn it the other way around. Action is not a callback, it does not consume an asynchronous result but it produces it. doTheMagic on the other hand is the callback, as it consumes the result (by alerting the result).
Thus, instead of passing Action as a "callback" to doTheMagic, you should be passing doTheMagic as a callback to Action.
function Run() {
var handler = new CallBackStringHandler();
Action(function(result) {
handler.doTheMagic(result);
});
// or, alternatively: (only in modern browsers supporting Function.bind)
Action(handler.doTheMagic.bind(handler));
};
Make Action accept a callback argument and call it when it's done. Finally, let doTheMagic just receive the result. I forked your fiddle, have a look!
Note: You won't get multi-threading using setInterval, it will still run in the same browser thread as the rest of your script. If you truly need to do some serious heavy lifting, you may want to use a web worker.
For most cases such as just concatenating a string like you're doing, this is overkill. Workers live in a completely separate environment and you can only communicate with them through messages, which adds quite a bit of complexity to your application. Make sure to do a good amount of testing and benchmarking before deciding that you really need a multi-threaded approach!
So to for a final answer I kinda resolved it this way (fork here):
function CallBackStringHandlerBy3rdParty() {};
CallBackStringHandlerBy3rdParty.prototype.doMagic = function(callback) {
var result = callback.call(this);
alert(result);
};
CallBackStringHandlerBy3rdParty.prototype.doMyOwnMagic = function(result) {
if(result.isComplete) {
this.doMagic(function(){return result.value;});
} else {
var that = this;
result.value += 1;
if(result.value < 10)
setTimeout(function(){that.doMyOwnMagic(result);},10);
else {
result.isComplete = true;
this.doMyOwnMagic(result);
}
}
};
function Run(){
var handler = new CallBackStringHandlerBy3rdParty();
var result = {};
result.value = 0;
result.isComplete = false;
handler.doMyOwnMagic(result);
};
Run();
Cheers!
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.
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 see there are lot's of threads here in SO about asking for a javascript sleep function and I know it can be done only using setTimeout and setInterval.
I do some userscripting with greasemonkey and written a script that loads a lot of pages and calculates something from them. It works, but I don't want to request the pages too fast.
var html0=syncGet(url0); // custom function for sync ajax call.
// fill the something array
for(var i=0;i<something.length;i++)
{
// calculate url1,url2 using the array and the i variable
// do something with lots of local variables
var html1=syncGet(url1);
// I would put a sleep here.
// do something with the results
var html2=syncGet(url2);
// I would put a sleep here.
// do something with the results
// get url3 from the page loaded from url2
var html3=syncGet(url3);
// I would put a sleep here.
// do something with the results
}
// use the result of the for loop and lots of code will follow...
The actual code is a bit more complex and longer than this.
I'm crying for the nonexistent sleep function (and understand why is it not possible) How to refactor this to use setTimeout, setInterval functions and keep it readable (and working) too?
For example this:
var urls = ["your","u","r","l´s"];
var htmls = new Array(urls.length);
var time = 1000;
for(var i=0;i<urls.length;i++){
(function(i){
setTimeout(function(){
htmls[i] = syncGet(urls[i]);
if(i == urls.length-1){
//continue here
}
},time*i);
})(i);
}
I had a similar problem where a big loop was blocking the whole browser in some older browsers, I solved it using :
function handlenext(idx,length) {
idx++
//do your stuff here base on idx.
if (idx < length) {
setTimeout(function(){handlenext(idx,length)},1)
} else {
initSuccessEnd()
}
}
var ln = something.length;
if (ln>0) {
handlenext(0,ln);
} else {
initSuccessEnd()
}
here initSuccessEnd is a callback function called when all is finished ..
After a research I think Mozilla's new iterator-generator stuff could be the most apropriate. (It's supported since FF2)
function doSomething()
{
//.....
var html=syncGet(url1);
yield true;
var html2=syncGet(url2);
yield true;
var html3=syncGet(url3);
yield true;
//......
yield false;
}
function iteratorRunner(iterator,timeout)
{
if (iterator.next())
{
setTimeout(function(){iteratorRunner(iterator,timeout)},timeout);
}
else
{
iterator.close();
}
}
var iterator=doSomething(); // returns an iterator immediately
iteratorRunner(iterator,1000); // runs the iterator and sleeps 1 second on every yield.
I hope greasemonkey will handle that...