Sharing variable JavaScript - javascript

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)

Related

Can I call an initialized object again?

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);

How to make a javascript FOR LOOP wait for certain conditions before looping

I have to call up a function (checkImdb) that will fetch some info from a php file (temp.php) and put some contents on a div (placeToFetchTo). This has to be done a certain amount of times, so I used a FOR LOOP for that.
The problem is that only the last instance of the looped counter (currentCastId) gets used. I understand there needs to be a way to force the FOR LOOP to wait for the fetch to be complete, and I have been looking online for answers but nothing seems to work so far. I apologise if I have missed an eventual answer that already exists.
Any help is appreciated.
This is the code I am referring to:
function checkImdb (totalCasts) {
$(function() {
for (currentCastId = 1; currentCastId <= totalCasts; currentCastId++) {
//Gets cast IMDB#
var row = document.getElementById("area2-" + currentCastId)
row = row.innerHTML.toString();
var fetchThis = "temp.php?id=" + row + "\ .filmo-category-section:first b a";
placeToFetchTo = "#area0-" + currentCastId;
function load_complete() {
var filhos = $(placeToFetchTo).children().length, newDiv ="";
var nrMoviesMissing = 0, looped = 0;
alert("done- "+ placeToFetchTo);
}
document.getElementById("area0").innerHTML = document.getElementById("area0").innerHTML + "<div id=\"area0-" + currentCastId + "\"></div>";
$(placeToFetchTo).load(fetchThis, null, load_complete);
} //End of: for (imdbLooper = 0; imdbLooper <= totalCasts; imdbLooper++) {
}); //End of: $(function() {
}
2017 update: The original answer had the callback arg as last arg in the function signature. However, now that the ES6 spread operator is a real thing, best practice is to put it first, not last, so that the spread operator can be used to capture "everything else".
You don't really want to use a for loop if you need to do any "waiting". Instead, use self-terminating recursion:
/**
* This is your async function that "does things" like
* calling a php file on the server through GET/POST and
* then deals with the data it gets back. After it's done,
* it calls the function that was passed as "callback" argument.
*/
function doAsynchronousStuff(callback, ...) {
//... your code goes here ...
// as final step, on the "next clock tick",
// call the "callback" function. This makes
// it a "new" call, giving the JS engine some
// time to slip in other important operations
// in its thread. This basically "unblocks"
// JS execution.
requestAnimationFrame(function() {
callback(/* with whatever args it needs */);
});
}
/**
* This is your "control" function, responsible
* for calling your actual worker function as
* many times as necessary. We give it a number that
* tells it how many times it should run, and a function
* handle that tells it what to call when it has done
* all its iterations.
*/
function runSeveralTimes(fnToCallWhenDone, howManyTimes) {
// if there are 0 times left to run, we don't run
// the operation code, but instead call the "We are done"
// function that was passed as second argument.
if (howManyTimes === 0) {
return fnToCallWhenDone();
}
// If we haven't returned, then howManyTimes is not
// zero. Run the real operational code once, and tell
// to run this control function when its code is done:
doAsynchronousStuff(function doThisWhenDone() {
// the "when done with the real code" function simply
// calls this control function with the "how many times?"
// value decremented by one. If we had to run 5 times,
// the next call will tell it to run 4 times, etc.
runSeveralTimes(fnToCallWhenDone, howManyTimes - 1);
}, ...);
}
In this code the doAsynchronousStuff function is your actual code.
The use of requestAnimationFrame is to ensure the call doesn't flood the callstack. Since the work is technically independent, we can schedule it to be called "on the next tick" instead.
The call chain is a bit like this:
// let's say we need to run 5 times
runSeveralTimes(5);
=> doAsynchronousStuff()
=> runSeveralTimes(5-1 = 4)
=> this is on a new tick, on a new stack, so
this actually happens as if a "new" call:
runSeveralTimes(4)
=> doAsynchronousStuff()
=> runSeveralTimes(4-1 = 3), on new stack
runSeveralTimes(3)
...
=> doAsynchronousStuff()
=> runSeveralTimes(1-1 = 0), on new stack
runSeveralTimes(0)
=> fnToCallWhenDone()
=> return
<end of call chain>
You need to use a while loop and have the loop exit only when all your fetches have completed.
function checkImdb (totalCasts) {
currentCastId = 1;
totalCasts = 3;
doneLoading = false;
while (!doneLoading)
{
//do something
currentCastId++;
if (currentCastId == totalCasts)
doneLoading = true;
}
}

JavaScript Asynch Callback - How to await the endresult

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!

How to refactor the javascript code when sleep like functionality needed?

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...

How to detect when one function is complete from another function?

I have a javascript function that is being built to animate the collapse of a div, and then proceed with other jobs. The code is as follows:
function newsFeed() {
var self = this;
this.collapse = function(listingID,orig_height,curr_height,opacity) {
var listing = document.getElementById(listingID);
var reduceBy = 5;
if(curr_height > reduceBy) {
curr_height = curr_height-reduceBy;
listing.style.overflow = "hidden";
listing.style.height = (curr_height-40) + "px";
if(opacity > 0) {
opacity = opacity - 10;
var opaque = (opacity / 100);
listing.style.opacity=opaque;
listing.style.MozOpacity=opaque;
listing.style.filter='alpha(opacity='+opacity+')';
}
setTimeout(function() { self.collapse(listingID,orig_height,curr_height,opacity); },1);
}else{
return true;
}
}
this.remove = function(listingID) {
var listing = document.getElementById(listingID);
var currHeight = listing.offsetHeight;
if (this.collapse(listingID,currHeight,currHeight,100)) {
// DO SOME OTHER STUFF
}
}
}
var newsFeed = new newsFeed();
newsFeed.remove('closeMe');
I cannot get the this.remove function to wait while this.collapse finishes and returns true. Is this impossible? What is the best way to go on?
Important: I would like to be able to use this.collapse with other functions yet to be built in the same fashion as I do here.
I cannot get the this.remove function to wait while this.collapse finishes
That is correct, it is impossible to do so. In JavaScript there is a single flow of execution. When the browser calls your code you can do some processing, but for anything further to occur (timeouts or event calls) you must return control to the browser.
‘Asynchronous’ processes like collapse() are done by setting timeouts, so control must be returned to the browser many times; when remove() calls collapse() the first time it returns immediately after the first timeout is set; that timeout cannot be fired until remove() itself returns, so your 'if' code will only ever execute if the very first call to collapse() was the last frame of animation (ie. the element was 5px or smaller already). Otherwise collapse()'s ‘return true’ will just be returning true to the browser's timeout-caller, which doesn't care at all what value you return to it.
Some languages give you tools such as threads or coroutines that can allow an asynchronous routine to be run from a synchronous routine; JavaScript does not. Instead, remove() must supply collapse() with a callback function it can call itself on the last frame.
There is no way you can pause the execution in Javascript till something else happens. All you can do is attach a callback function to collapse to call after it is done executing the final step.
As a sidenote, jQuery provides functions like fade(), animate() etc and supports queuing. If you don't want to use jQuery, you can still look at the code to see how it's implemented.
See the examples in this page.
setTimeout is not a "sleep". The function will end right there and return "undefined".
To manage that, I think you should do something like:
var newsFeed = new newsFeed();
newsFeed.onaftercollapse = function () {
newsFeed.remove('closeMe'); // "newsFeed" or "self"? must test
};
And then instead of return true;, the collapse() will end with:
if (self.onaftercollapse) self.onaftercollapse();
This example demonstrates how to check if a function is complete.
function foo() {
foo.complete = false;
// your code here
foo.complete = true;
}
foo.complete = false;
if (foo.complete) { // foo execution complete
// your code here
}
This code demonstrates how to check if a function has been run once.
function foo() {
// your code here
foo.ranOnce || (foo.ranOnce = true);
}
foo.ranOnce = false;
if (foo.ranOnce) { // foo execution complete at least once
// your code here
}

Categories

Resources