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!
Related
I have this function:
waitForFreeAccnt.prototype.isMemberFree = function () {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return true;
} else {
console.log('it is not free');
return false;
}
});
};
I would like to wait till the account is free for up to 10 seconds with something like that:
var test = function () {
for (var start = 1; start < 10; start++) {
var result = self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
But it doesn't work because self.api.getMemberInfo is asynch call. This is super frustrating with Javascript. Any other language it would be so simple to do. How do I force the for loop to wait for self.isMemberFree() to finish executing before proceeding with the loop?
Also to note, this is not in browser execution so I don't care about anything hanging.
When dealing with asynchronous code, you need to make use of callbacks. That is, if you want to do a() and b() in order but a() does something asynchronously, then you need to call b() from within a() once a() has a result. So not:
a(); // does something asynchronously
b(); // tries to use a()'s result but it isn't available yet
... but rather
a(b); // pass b to a() and a() will call it when ready
function a(callback) {
triggerAsyncFunction(function(result) {
if (result === something)
callback("a just finished");
});
}
Note that a() doesn't refer to b() by name, it just calls whatever function is passed in as an argument.
So applying that to your code, maybe something like this:
waitForFreeAccnt.prototype.isMemberFree = function (cbf) {
var self = this;
self.api.getMemberInfo(function () {
cbf(self.api.connect.accountType === 'FREE');
});
};
waitForFreeAccnt.prototype.testMemberXTimes = function(maxAttempts, callback) {
var attempts = 0;
var self = this;
(function attempt() {
self.isMemberFree(function(free) {
if (free)
callback(true);
else if (++attempts < maxAttempts)
setTimeout(attempt, 1000);
else
callback(false);
});
)();
};
this.testMemberXTimes(10, function(isFree) {
// the next part of your code here, or called from here
// because at this point we know we've tested up to
// ten times and isFree tells us the result
});
Note that the way I coded getMemberInfo() it is basically doing the same thing yours was, but instead of returning a boolean it is calling the callback function and passing the same boolean value that you were returning. (I've removed the console.log()s to make the code shorter.)
Note also that you could structure the above to use promises, but the end result will be the same.
You could return a Promise
waitForFreeAccnt.prototype.isMemberFree = function () {
return new Promise((reject, resolve)=>
// set a timeout if api call takes too long
var timeout = setTimeout(()=> reject(Error('API timeout')), 10000);
// make api call
this.api.getMemberInfo(()=> {
clearTimeout(timeout);
resolve(this.api.connect.accountType === 'FREE');
});
);
};
Then use it like this
whatever.isMemberFree().then(isFree=> {
if (isFree)
console.log('it is free');
else
console.log('it is not free');
})
// handle timeout or other errors
.catch(err=> {
console.log(err.message);
});
Building on naomik's answer, if you do it that way you can pretty easily use a for loop with it, using the (most likely) upcoming async/await feature - though it's not part of ES2015.
// Note "async" here! That will make "await" work. It makes the function
// return a promise, which you'll be able to either "await" or
// "test().then" later.
var test = async function () {
for (var start = 1; start < 10; start++) {
// Right here we're using "await" - it makes JavaScript *wait* for
// the promise that comes from self.isMemberFree() to be finished.
// It's really handy because you can use it in loops like "for" and
// "while" without changing the flow of your program!
var result = await self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
For now you'll need to use a transpiler like Babel or Traceur before you can really use async/await, though. It's only supported in Microsoft Edge 14 right now.
And a big emphasis that what is returned from test() isn't whatever you directly return from inside it. If I do this:
var test = async function() { return 15; };
var result = test();
I'm not going to get 15 - I'll get a promise that will resolve as 15:
result.then(function(res) {
console.log(res); // 15
});
// or, use an async function again:
var main = async function() {
console.log(await res); // 15
};
main();
I don't have my work laptop today because it is Sunday, I'm coding this on sublime. Apologise if the syntax is a bit off.
To solve your problem I would recommend changing isMemberFree() to take in a callback function. This is because isMemberFree is async, and you will need a way to report the result after it has done the work.
Then change test function to use setTimeout API to wait a second.
Wrap the function call for isMemberFree() to be in a nested function and call it recursively, that way you'll have synchronize control over the async calls.
Look at the coding example:
waitForFreeAccnt.prototype.isMemberFree = function (done) {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return done(null, true);
} else {
console.log('it is not free');
return done(null, false);
}
});
};
var test = function () {
var testMembership = function(waitAttempt, isFree) {
if (isFree) {
return;
}
else if (waitAttempt > 10) {
// wait exceeded, do something.
return;
}
setTimeout(function() {
self.isMemberFree(function(err, isFree) {
testMembership(waitAttempt+=1, isFree);
});
}, /*total milliseconds in 1sec=*/1000);
}
testMembership(/*WaitAttempts=*/0, /*isFree=*/false);
};
What the above code does is that, presumably something has already been done to the member's account and now test function is called. So it waits for 1 second, then call isMemberFree function, this happens recursively until either isMemberFree() returns true OR the 10 seconds wait has been exceeded.
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 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.
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...
In following code, I am taking input
of an AJAX call into a function called
plr(). I want to detect when loading
is complete using the done variable.
But main thread is locking the
variable and the script hangs the
browser. If I put the alert in the
commented place, the purpose is
served. So, what other way can I use
to do the same?
function openX() {
LoadContentInto("Default.aspx", plr);
var obj = null;
done = false;
function plr() {
x = this.AJAXObject.responseText;
t = x.indexOf('{')
n = parseInt(x.substring(0, t));
s = x.substring(t, n + t);
p = eval('(' + s + ')');
obj = p;
done = true;
}
while (done != true)
{ // alert("hello");
}
alert(done);
}
Basically you have to make synchronous your ajax call, so there's no need to create an empty (blocking) while. the callback plr() will be executed on successful response, then remaining data will be called inside that callback
http://www.hunlock.com/blogs/Snippets:_Synchronous_AJAX
You should not wait that actively for the result. When the AJAX call is successfully finished you have the callback function called. In your case it seems that it is plr (although it is not clear what LoadContentInto exactly does).
It seems you have a temptation to make the AJAX success callback synchronous. Sometimes I used to have such passions, but so far it always showed up that there is an asynchronous way as well.
Maybe you want something like that:
function openX() {
LoadContentInto("Default.aspx", plr);
var obj = null;
var done = false; // you have your variable global! Make it local!
function plr() {
x = this.AJAXObject.responseText;
// ...
// put your code here
// ...
alert("Done!");
done = true;
}
setTimeout(function(){
if (!done) {
alert("Please wait!");
// Does the response and/or the operation after the responseText arives take a long time?
// Based on that decide how to inform the user
}
}, 100); // Set the timeout to right value.. based on your needs
}
Few comments to your code:
you have done declared as a global variable, it is very likely that it should be local
while (done != true) is much cleaner as while (!done)