This question already has answers here:
Calling functions with setTimeout()
(6 answers)
Closed 6 years ago.
I wrote a script that will read in a text file and attempt to find duplicate entries in the dataset with the user-entered parameters. As the dataset is quite large, the page tends to freeze. I referenced this post here:
Prevent long running javascript from locking up browser
To perform my calculations in chunks using setTimeout(). I am trying to get a percentage complete to display on the page, but it won't display until the script finishes running (both Chrome and Firefox).
This is my pump function:
function pump(initDataset, dataset, output, chunk){
chunk = calculateChunk(initDataset, dataset, output, chunk);
document.getElementById("d_notif_container").style.display="block";
document.getElementById("d_percent").innerHTML = NOTIF_SEARCH_START + percentComplete + "%"; //This is a div
document.getElementById("i_pc").value = percentComplete; //This is an input text field
if(percentComplete < 100){
console.log("Running on chunk " + chunk);
setTimeout(pump(initDataset, dataset, output, chunk), TIMEOUT_DELAY);
}
else {
comparisonComplete(output);
}
}
I attempted to display in two different ways, using innerHTML and value (two different elements). The percentComplete is a global variable that is updated in calculateChunk().
The strangest thing is, if I inspect one of those elements, I can watch the HTML change counting the percent (in Chrome). It just simply does not show on the actual page until the script is done running. I've tried changing the timeout up to 1000 as well with not luck.
Any idea why this is happening?
This code
setTimeout(pump(initDataset, dataset, output, chunk), TIMEOUT_DELAY);
calls pump and passes its return value into setTimeout, exactly the way foo(bar()) calls bar and passes its return value into foo.
If you want to call pump after a timeout with those values, you can use Function#bind to do it:
setTimeout(pump.bind(null, initDataset, dataset, output, chunk), TIMEOUT_DELAY);
// ------------^^^^^^^^^^
Function#bind returns a new function that, when called, will use the given this value (the first argument) and arguments you give bind.
Related
I have a textarea that the user keys in some data. I need to read this data when it is changed and process it, the processing is very time consuming.
I have the textarea events: change keyup cut paste input call the function triggerProcess().
If one character only is typed into the textarea (or one character only is deleted) then needs to be get called triggerProcess().
If a word of several characters is typed in then the whole system grinds to a halt if triggerProcess() gets called for every character in that word. Even if the system did not grind to a halt it is pointless having triggerProcess() called for each character as the whole word plus the entire contents is what is required to be processed. However a single character such as "I", or a number may need to be processed.
I want to postpone the processing if the typist is typing a word or sentence (if the typist is fast ) but once they pause then their data is processed.
As soon as triggerProcess is called the first thing it does is clear a timer and then set another timer that calls the intensive processing when it expires.
My idea is to put a short timeout in triggerProcess of about 300ms so that there is always at least 300ms before the data is read from the textarea and processed. If one character say "I" is typed then that will be processed, but if several characters are typed in a sequence such as when a word is typed then triggerProcess is called by the textarea event watchers, the timer is cleared and started again for each character. When the word is finished and there is a gap in the stream from the keyboard into the text area the last started timer expires and the word is processed. I know that this will freeze the typist out while the text area contents is being processed, but that freeze out is much better than having the typist frozen out for each character.
How does my code look? Could something go terribly wrong? Could it be improved?
Thank you for taking the time to read this, all comments gratefully received.
Peter
var delayTimer; //global variable as opposed to local variable so that it can be cleared within function display.
function triggerProcess(){
clearTimeout(delayTimer);
delayTimer = 0;
delayTimer=setTimeout(function(){
// when the Timeout fires then
// read the data from the textarea and have some very time consuming work on it
},300);
}
What you are trying to achieve is called autocomplete.
Your code looks cool to me, but If I were you, I will not go for setTimeOut because that is a hack and will make the experience slow. I would think of AJAX call rather than a setTimeOut With AJAX (since the call is asynchronous), you can send request as many times as you want without user experiencing anything bad. Whenever the user types and change event will be called, it will ask for new matching words and will show the new matching words to user, when it has any.
I do not know if you use jQuery or not, but anyhow jQuery UI has one component (See here jQuery UI AUtocomplete). Since thank god jQuery UI is an open-source project, you can see the code at /ui/jquery.ui.autocomplete.js. If you open the other autocompleteEvents.js, you can see that there are four event types there: focus, close, select, change. As you can see in autocomplete example.
$.ajax({
//Where you load the data, could be a backend server instead of a
// XML file
url: "london.xml",
dataType: "xml",
success: function( xmlResponse ) {
//If matching any element from the london.xml file,
// Show the result in a menu or something
}
});
It is just calling whenever anything gets changed to get the new results (as simple as that). If you do not like AJAX, try to use callbacks, show the results after you sent the newly typed word and get a reply back and not in between.
Here is just another autocomplete example with my second suggestion in mind: complete.ly. It shares the same concept.
It just adds the onchange and keyup event:
if (txt.addEventListener) {
txt.addEventListener("input", handler, false);
txt.addEventListener('keyup', handler, false);
txt.addEventListener('change', handler, false);
}
And will make the callback whenever it is done with getting new values, checking if anything matches and will show the result:
var handler = function() {
var value = txt.value;
if (registerOnTextChangeOldValue !== value) {
callback(value);
}
};
Read the full source here: complete.ly source code
I hope I make sense and my answer helped!
I am trying to get a certain area of data out from ckeditor. In order to do that I use the following code
function get_body_html(){
var email = CKEDITOR.instances['message'].getData();
var before_body = header_to + to + to_subject + subject + subject_body;
var s_index = email.indexOf(before_body)+before_body.length;
var e_index = email.indexOf(body_footer);
return email.substring(s_index,e_index);
}
For some reason that works when I do this on page load
CKEDITOR.instances.message.setData(header_to + to + to_subject+
subject + subject_body + body_text + body_footer);
get_body_html();
it works correctly and gives me the same string that is contained in body_text.
But when I do this
body_text = get_body_html();
CKEDITOR.instances.message.setData(header_to + to + to_subject + subject +
subject_body + body_text + body_footer);
in an onclick function it gets the wrong indexs somehow. Sometimes it can't find the string and returns -1 other times it just gets a weird index that doesn't make sense. These index variations only happen when my code is changed to tackle the problem a different way. So if it is the wrong indices like -5 and 2 then those would continue to be the wrong indices until I made a code change.
There are two facts that you should know about editor.setData.
In some cases it is asynchronous (it depends on the type of editor). That's why it also accepts a callback. Therefore any code that is meant to be executed after setData() should be executed in that callback.
It never is asynchronous before editor is ready. In this period (between editor initialization and instanceReady event) it works in a different mode - it just caches the set value and on getData() it returns exactly that value.
So, as I see on page load you call synchronously setData() and getData() - your function works because you get the value you're expecting to get.
But then, when you try to getData() when editor is already ready you get the HTML parsed, fixed, processed and perhaps differently formatted by CKEditor. I guess that your indexOf() checks are not enough to handle this. You have to rethink your function - e.g. regexp can help.
What also can help is removing htmlwriter plugin, which formats HTML in a way which may make it harder for you to work with it. E.g.:
config.removePlugins = 'htmlwriter';
I was able to get it to work. So the htmlwriter was one of the problems because it must add spaces in between by HTML tags. The other issue I found is that it strips some of the semicolons out in some of the style attributes. Overall CKEditor does a lot of formatting of the source which makes it very hard to index correctly but it's pretty much a trial and error thing. I ended up using the search JavaScript method for strings which can take a regular expression but I used it the same way indexOf would be used so I don't really know if that made a difference or not.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript Memory Limit
I'm working on creating html page using client side javascript which will load around 150mb of XML data file on page load. When the file size was around 5 mb, it took 30 seconds to load whole data into an array. But when I changed file to 140 mb, the page is not responding in firefox and crashing abruptly in chrome. My snippet to load data will process on every individual tag in xml. My question is, is there any limited heap size for javascript? Any academic article resource is preferable to emphasize my research.
$(document).ready(function () {
// Open the xml file
$.get("xyz.xml", {}, function (xml) {
// Run the function for each in the XML file
$('abc', xml).each(function (i) {
a = $(this).find("a").text();
b = $(this).find("b").text();
c = $(this).find("c").text();
ab = $(this).find("ab").text();
bc = $(this).find("bc").text();
cd = $(this).find("cd").text();
de = $(this).find("de").text();
// process data
dosomething(a,b,c,ab,bc,cd,de);
}); }); });
I don't know of any limits. I've been able to load even a 1Gb file. Yes, it was slow to load initially and everything ran slowly because most of the memory will be paged.
However, there are problems with trying to load a single JavaScript object that is that big, mostly because the parsers can't parse an object that is too big. See Have I reached the limits of the size of objects JavaScript in my browser can handle?
For that case, the solution was to break up the creation of the JavaScript object into multiple stages rather than using a single literal statement.
Because it's to much to improve. First of all i'd like to recommend you a post 76 bytes for faster jQuery. So, relying on that replace your $(this) on $_(this).
it will save you a lot memory and time!!
If you don't want to use single jQuery object, please cashe you variable like that:
$('abc', xml).each(function (i) {
var $this = $(this);
a = $this.find("a").text();
....
});
and you can provide your dosomething function to try to improve it
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I'm investigating a potentially large incident of advertising fraud and in following the bread crumbs have come across a page that calls a javascript aptly named "Google_Analytics.js", which it clearly is not.
I've put the raw code to the file here: http://pastebin.com/5YqzCVbB
I am not skilled enough with javascript to have seen something like this before or to know what it is designed to do.
Is there anyone here who can help?
Thanks in advance for any assistance!
D
Well, it's not Google Analytics, but it looks like it's purposely trying to spoof GA into thinking something's being clicked more than it should be or something.
What is in this? Well, there's a bunch of code related to creating a Flash object. And a big whitelist, presumably of pages this should affect. And there's this:
if (follow.length > 0) {
rand = randomFromTo(100, 700);
rand2 = randomFromTo(1, 6);
if (rand == 666) {
$('<iframe />', {
name: 'follow_analytics',
id: 'follow_analytics',
style: 'width:2px;height:2px;display:none;',
width: '2',
height: '2',
src: follow[0]
}).appendTo('body');
if (trk_value[$.inArray(1 * (follow[0].match(/cid=([\d]*)/)[1]), tracking)]) {
if (rand2 == 1) {
$('<iframe />', {
name: 'trk_analytics',
id: 'trk_analytics',
style: 'width:2px;height:2px;display:none;',
width: '2',
height: '2',
src: 'tracking.html?' + trk_value[$.inArray(1 * (follow[0].match(/cid=([\d]*)/)[1]), tracking)]
}).appendTo('body');
}
}
};
}
This looks like it's meant to artificially inflate numbers just a little bit: one part in 600. And one in 6 of those should be tracked or something. But I don't know a lot about Google Analytics so I can't help you beyond that.
The short answer is, it attempts to find a URL on the page from one of various sources (global Javascript variable, Flash params, and iframe src). If it finds said URL, and the correct random number is chosen, (666, randomly chosen between 100 and 700), then it creates, on the page, an iframe of that URL.
Then, it looks for a "CID" in the URL that was found in the first of two static lists of numeric values. If the CID is found in that list, it gets the corresponding numeric value from the other list. Then, if a second correct random number is chosen, (1, randomly chosen between 1 and 6), it will attach a second iframe. The URL of that iframe will be tracking.html?x, where x is the numeric value found from the translation of the CID.
Neither iframe is intended to be seen.
That's the short answer, but if you need more details, see below. I really hope you can get some value out of this, because I don't know enough about ads and ad behaviour to understand what it's really trying to accomplish.
First, it tries to create a function, but that appears to be a decoy, as all it does is create a string that's supposed to look like code. Also, it never gets called.
var analytics_obj=function(){
var b='undefined",Q="object",n="Shockwave Flash",p="ShockwaveFlash.ShockwaveFlash",P="application/x-shockwave-flash",m="SWFObjectExprInst",j=window,K=document,T=navigator,o=[],N=[],i=[],d=[],J,Z=null,M=null,l=null,e=false,A=false;var h=function(){var v=typeof K.getElementById!=b&&typeof K.getElementsByTagName!=b&&typeof K.createElement!=b,AC=[0,0,0],x=null;if(typeof T.plugins!=b&&typeof T.plugins[n]==Q){x=T.plugins[n].description;if(x&&!(typeof T.mimeTypes!=b&&T.mimeTypes[P]&&!T.mimeTypes[P].enabledPlugin)){x=x.replace(/^.*\s+(\S+\s+\S+$)/,"$1");AC[0]=parseInt(x.replace(/^(.*)\..*$/,"$1"),10);AC[1]=parseInt(x.replace(/^.*\.(.*)\s.*$/,"$1"),10);AC[2]=/r/.test(x)?parseInt(x.replace(/^.*r(.*)$/,"$1"),10)}}else{if(typeof j.ActiveXObject!=b){var y=null,AB=false;try{y=new ActiveXObject(p+".7")}catch(t){try{y=new ActiveXObject(p+".6");AC=[6,0,21];y.AllowScriptAccess="always"}catch(t){if(AC[0]==6){AB=true}}if(!AB){try{y=new ActiveXObject(p)}catch(t){}}}if(!AB&&y){try{x=y.GetVariable("$version");if(x){x=x.split(" ")[1].split(",");AC=[parseInt(x[0],10),parseInt(x[1],10),parseInt(x[2],10)]}}catch(t){}}}}var AD=T.userAgent.toLowerCase(),r=T.platform.toLowerCase(),AA=/webkit/.test(AD)?parseFloat(AD.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1"))false,q=false,z=r?/win/.test(r),w=r?/mac/.test(r)/mac/.test(AD);}'
};
Note that var b= starts with a single quote, and the line ends with a single quote. Nowhere in the middle is there any other single quote, so it is just one long string meant to look like code. Unless it's meant to be run through eval() (and I don't see an eval() anywhere in this script), it's just a decoy.
Second, it creates a function that's a simple shorthand for getting a random number.
Third is the meatiest part. It tries to capture a URL. First it tries a variable called click_url2, which is not declared here, so it must be trying to rip it off from another script.
Failing that, it tries to find a flash movie on the page that has clickTag= or click= in its FlashVars or URL, and capture the value after the =.
Failing that, it looks for an iframe that has an src with the following text in it: click= or CPSC#=, followed by http://media.fastclick.net/w/click.here and forced_click=, and captures all of that text starting with http, and ending at forced_click=. It appends this with the URL: http://cc.openxads.org/clothedcanines.com/popped_content.html.
This behaviour is below:
try {
follow.push(click_url2);
} catch(e){
try {
follow.push(unescape($('object param[name="flashvars"]').attr('value').match(/clickTag=([^&]*)/)[1]));
} catch(e){
try {
follow.push(unescape($('object param[name="movie"]').attr('value').match(/clickTag=([^&]*)/)[1]));
} catch(e){
try {
follow.push(unescape($('object param[name="FlashVars"]').attr('value').match(/clickTag=([^&]*)/)[1]));
} catch(e){
try {
follow.push(unescape($('embed').attr('flashvars').match(/clickTag=([^&]*)/)[1]));
} catch(e){
try {
follow.push(unescape($('embed').attr('flashvars').match(/click=([^&]*)/)[1]));
} catch(e){
try {
follow.push(unescape($('iframe').attr('src').match(/click=(http:\/\/media\.fastclick\.net\/w\/click\.here.*forced_click=)/)[1])+"http%3A%2F%2Fcc.openxads.org%2Fclothedcanines.com%2Fpopped_content.html");
} catch(e) {
try {
follow.push(unescape($('iframe').attr('src').match(/CPSC#=(http:\/\/media\.fastclick\.net\/w\/click\.here.*forced_click=)/)[1])+"http%3A%2F%2Fcc.openxads.org%2Fclothedcanines.com%2Fpopped_content.html");
} catch(e) {};
};
};
};
};
};
};
};
After that, it declares two very long arrays of numbers. I have no clue as to what they are, but based on later behaviour, they seem to be parallel.
If any of the above conditions (the variable, flash, or iframe) were found, it continues. If not it fails.
It will at this point choose two random numbers, the first between 100 and 700, and the second between 1 and 6.
If the first number chosen is 666, it will create an iframe on the page, very small and hidden, not intended to be viewed. That iframe's src will be equal to whatever URL it managed to capture above.
if(rand==666){
$('<iframe />', {
name:'follow_analytics',id:'follow_analytics',style:'width:2px;height:2px;display:none;',width:'2',height:'2',src:follow[0]
}).appendTo('body');
Then (still if the first number is 666), comes the tricky part. It will try to find a cid= in the URL it captured earlier, and capture the (expectedly numeric) value of it. This numeric value will be checked against one of the arrays declared earlier. If found, it will get the value from the other array at the same index as the value it found. Essentially, this is just a translation table, changing the so-called CID into a different but corresponding numeric value.
So if that one matched, AND if the second random number (between 1 and 6) is 1, only then will it attach a second iframe. The second iframe's src will be tracking.html?, appended by the numeric value it got from the second array, whose value was gotten by matching it from the first array.
if(trk_value[$.inArray(1*(follow[0].match(/cid=([\d]*)/)[1]),tracking)]){
if(rand2==1){
$('<iframe />', {
name:'trk_analytics',id:'trk_analytics',style:'width:2px;height:2px;display:none;',width:'2',height:'2',
src:'tracking.html?'+trk_value[$.inArray(1*(follow[0].match(/cid=([\d]*)/)[1]), tracking)]
}).appendTo('body');
}
}
I am writing a greasemonkey script. Recently i had this same problem twice and i have no idea why is this happening.
function colli(){
.....
var oPriorityMass = bynID('massadderPriority');//my own document.getElementById() function
var aPriorities = [];
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
.....
}
So the mystery of this is, one day i had oPriorityMass named as oPririoty. It was working fine, but the whole function was not yet complete and i started working on another functions for my script. These functions have no connection with each other.
Few days later i decided to go back to my function in the above example and finish it. I ran a test on it without modifying anything and got an error in the firefox's (4) javascript error console saying that oPriority.chilNodes[cEntry] is undefined. NOTE, few days back i have tested it exactly the same way and there was no such problem at all.
Ok, so, i decided to rename oPriority to oPriorityMass. Magically, problem got solved.
At first i thought, maybe there was some conflict of 2 objects, with the same name being used in different functions, which somehow continued to live even outside of function scope. My script is currently over 6000 lines big, but i did a search and found out that oPriority was not mentioned anywhere else but in this exact function.
Can somebody tell me, how and why is this happening? I mentioned same thing happened twice now and they happened in different functions, but the same problem node.childNodes[c] is undefined yet node is not null and node.childNodes.length show correct child count.
What is going on? How do i avoid such problems?
Thank you
EDIT: The error given by error console is
Error: uncaught exception: TypeError: oPriorityMass.childNodes[cEntry] is undefined
In response to Brocks comment:
GM_log(oPriorityMass.childNodes[cEntry]) returns undefined as a message. So node.childNodes[c] is the thing that is undefined in general.
My script creates a div window. Later, the above function uses elements in this div. Elements do have unique IDs and i am 100% sure the original site don't know about them.
My script has a start/stop button to run one or the other function when i need to.
I have been refreshing the page and running my script function now. I have noticed that sometimes (but not always) script will fail with the described error on the first run, however, if i run it again (without refreshing the page) it starts working.
The page has a javascript that modifies it. It changes some of it's element widths so it changes when the browser is resized. But i know it has no effect on my div as it is left unchanged when i resize browser.
EDIT2:
function bynID(sID) {
return top.document.getElementById(ns(sID));
}
function ns(sText) {
return g_sScriptName + '_' + sText;
}
ns function just adds the script name in front of the ID. I use it when creating HTML element so my elements never have the same id as the web page. So bynID() is simple function that saves some typing time when i need to get element by ID.
I have modified my colli() function to include check
if (oPriorityMass) {
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
}
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
The loadPage function does 1 AJAX call, then i run few XPATH queries on it, but the actual contents are never appended/shown on the page, just kept inside document.createElement('div'), then this function calls colli(). So now, as i have modified my function, i checked the error console and saw that it may take up to 5 tries for it to start working correctly. 5 x 2seconds, thats 10 seconds. It is never 5 retries always, may vary There's got to be something else going on?
In Firefox, childNodes can include #text nodes. You should check to make sure that childNodes[cEntry] has nodeType == 1 or has a getAttribute method before trying to call it. e.g.
<div id="d0">
</div>
<div id="d1"></div>
In the above in Firefox and similar browsers (i.e. based on Gecko and WebKit based browsers like Safari), d0 has one child node, a text node, and d1 has no child nodes.
So I would do something like:
var sCollNumber, el0, el1;
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
el0 = oPriorityMass.childNodes[cEntry];
// Make sure have an HTMLElement that will
// have a getAttribute method
if (el0.nodeType == 1) {
sCollNumber = el0.getAttribute('coll');
el1 = bynID('adder' + sCollNumber + '_check');
// Make sure el1 is not falsey before attempting to
// access properties
if (el1 && el1.checked)
// Never call parseInt on strings without a radix
// Or use some other method to convert to Number
aPriorities.push(parseInt(sCollNumber, 10));
}
}
Given that sCollNumber seems like it is a string integer (just guessing but it seems likely), you can also use:
Number(sCollNumber)
or
+sCollNumber
whichever suits and is more maintainable.
So, according to your last edit, it now works, with the delay, right?
But when I suggested the delay it was not meant to do (even more?) ajax calls while waiting!!
NOT:
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
More like:
setTimeout (colli, 2000);
So the ajax and the other stuff that loadPage does could explain the excessive delay.
The random behavior could be caused by:
return top.document.getElementById(ns(sID));
This will cause erratic behavior if any frames or iframes are present, and you do not block operation on frames. (If you do block such operation then top is redundant and unnecessary.)
GM does not operate correctly in such cases -- depending on what the script does -- often seeming to "switch" from top scope to frame scope or vice versa.
So, it's probably best to change that to:
return document.getElementById (ns (sID) );
And make sure you have:
if (window.top != window.self) //-- Don't run on frames or iframes
return;
as the top lines of code.
Beyond that, it's near impossible to see the problem, because of insufficient information.
Either boil the problem into a Complete, Self Contained, Recipe for duplicating the failure.
OR, post or link to the Complete, Unedited, Script.