What Chrome does instantly, Firefox takes 30 seconds - javascript

Currently, I am creating a program that will turn source code into highlighted HTML-like text. When I tested it, though, I found some strange results. On Chrome, the program will parse 1000 lines of source almost instantaneously. Firefox, however, takes 30 seconds to parse the same 1000 lines. And, ironically enough, IE10 only takes 18 seconds.
Now, I understand that different browsers implement javascript differently and that Chrome tends to be faster, but I do not understand why it is taking Firefox over 30 times longer. I ran a raw while-loop test of 10,000,000,000 operations on each, and it took FF 14 seconds and Chrome 12. Therefore, I am inclined to believe that somewhere in my code is something that takes Firefox an abnormally long time to accomplish; I've done research, but nothing I've found so far would indicate the large discrepancy I am seeing.
So, does anyone have any suggestions as to what may be causing this? I've posted the problem area of the code below (commenting this portion out causes both browsers to parse instantaneously). start and end are both regular expressions; istream is where the source code is coming from, and ostream is where parsed code goes to. istream.read() calls the String slice() method. Finally, this function is called many many times throughout the program.
function(buffer, istream, ostream){
if(start.test(istream.content)){
buffer = istream.read();
ostream.write('[[span class="' + type + '"]]' + buffer);
do{
/* Special Cases */
if(end.test(ostream.content + istream.peek()) && (istream.peek() == "\n" || istream.peek() == " " || istream.peek() == "\t")){
include = true;
break;
}
else if(istream.peek() == "\n"){
istream.read();
ostream.write('[[/span]][[/span]]\n[[span class="line"]][[span class="' + type + '"]]');
continue;
}
else if(istream.peek() == "\t"){
istream.read();
ostream.write("#<    >#");
continue;
}
else if(istream.peek() == " "){
istream.read();
ostream.write("#< >#");
continue;
}
ostream.write(istream.read());
} while(!istream.isEmpty() && !end.test(ostream.content));
if(include || istream.isEmpty())
ostream.write('[[/span]]');
else{
var ending = ostream.content.length-1;
while(!end.test(ostream.content.substr(ending)))
--ending;
istream.content = ostream.content.substr(ending) + istream.content;
ostream.content = ostream.content.substring(0, ending) + '[[/span]]';
}
return true;
}
return false;
}
Any insight would be greatly appreciated, and if you have any queries as to how certain aspects of this are implemented, I will oblige. Thanks in advance.
Definition of istream and ostream objects:
function IOstream(init){
this.content = init;
this.read = function(){
var tmp = this.content.charAt(0);
this.content = this.content.slice(1);
return tmp;
};
this.peek = function(){ return this.content.charAt(0); };
this.write = function(str){ this.content += str; };
this.isEmpty = function(){ return this.content.length == 0; }
}

I think it is because on every .read() call you make content.slice(1) and every time it copies the entire string but first character and can take a lot of time.
Try modifyin your IOStream class like this:
function IOstream(init){
this.content = init;
this.cursor = 0;
this.read = function(){
var tmp = this.content.charAt(this.cursor);
this.cursor++;
return tmp;
};
this.peek = function(){ return this.content.charAt(this.cursor); };
this.write = function(str){ this.content += str; };
this.isEmpty = function(){ return this.cursor>=this.content.length; }
}
I think it will solve your speed problem in all browsers.

Noticed you're using loose equality. I would start there and change the == to === and see if it makes a difference.
Here is a jsperf of loose vs strict: http://jsperf.com/performance-of-loose-vs-strict-equality

Related

Using FileReader.readAsArrayBuffer() on changed files in Firefox

I'm running into an odd problem using FileReader.readAsArrayBuffer that only seems to affect Firefox (I tested in the current version - v40). I can't tell if I'm just doing something wrong or if this is a Firefox bug.
I have some JavaScript that uses readAsArrayBuffer to read a file specified in an <input> field. Under normal circumstances, everything works correctly. However, if the user modifies the file after selecting it in the <input> field, readAsArrayBuffer can get very confused.
The ArrayBuffer I get back from readAsArrayBuffer always has the length that the file was originally. If the user changes the file to make it larger, I don't get any of the bytes after the original size. If the user changes the file to make it smaller, the buffer is still the same size and the 'excess' in the buffer is filled with character codes 90 (capital letter 'Z' if viewed as a string).
Since this code is so simple and works perfectly in every other browser I tested, I'm thinking it's a Firefox issue. I've reported it as a bug to Firefox but I want to make sure this isn't just something obvious I'm doing wrong.
The behavior can be reproduced by the following code snippet. All you have to do is:
Browse for a text file that has 10 characters in it (10 is not a magic number - I'm just using it as an example)
Observe that the result is an array of 10 items representing the character codes of each item
While this is still running, delete 5 characters from the file and save
Observe that the result is still an array of 10 items - the first 5 are correct but the last 5 are all 90 (capital letter Z)
Now added 10 characters (so the file is now 15 characters long)
Observe that the result is still an array of 10 items - the last 5 are not returned
function ReadFile() {
var input = document.getElementsByTagName("input")[0];
var output = document.getElementsByTagName("textarea")[0];
if (input.files.length === 0) {
output.value = 'No file selected';
window.setTimeout(ReadFile, 1000);
return;
}
var fr = new FileReader();
fr.onload = function() {
var data = fr.result;
var array = new Int8Array(data);
output.value = JSON.stringify(array, null, ' ');
window.setTimeout(ReadFile, 1000);
};
fr.readAsArrayBuffer(input.files[0]);
//These two methods work correctly
//fr.readAsText(input.files[0]);
//fr.readAsBinaryString(input.files[0]);
}
ReadFile();
<input type="file" />
<br/>
<textarea cols="80" rows="10"></textarea>
In case the snippet does not work, the sample code is also available as a JSFiddle here: https://jsfiddle.net/Lv5y9m2u/
Interesting, looks like Firefox is caching the buffer size even the file is modified.
You can refer to this link, replaced readAsArrayBuffer with is custom functionality which uses readAsBinaryString. Its working fine in Firefox and Chrome
function ReadFile() {
var input = document.getElementsByTagName("input")[0];
var output = document.getElementsByTagName("textarea")[0];
if (input.files.length === 0) {
output.value = 'No file selected';
window.setTimeout(ReadFile, 1000);
return;
}
var fr = new FileReader();
fr.onload = function () {
var data = fr.result;
var array = new Int8Array(data);
output.value = JSON.stringify(array, null, ' ');
window.setTimeout(ReadFile, 1000);
};
fr.readAsArrayBuffer(input.files[0]);
//These two methods work correctly
//fr.readAsText(input.files[0]);
//fr.readAsBinaryString(input.files[0]);
}
if (FileReader.prototype.readAsArrayBuffer && FileReader.prototype.readAsBinaryString) {
FileReader.prototype.readAsArrayBuffer = function readAsArrayBuffer () {
this.readAsBinaryString.apply(this, arguments);
this.__defineGetter__('resultString', this.__lookupGetter__('result'));
this.__defineGetter__('result', function () {
var string = this.resultString;
var result = new Uint8Array(string.length);
for (var i = 0; i < string.length; i++) {
result[i] = string.charCodeAt(i);
}
return result.buffer;
});
};
}
ReadFile();
I think you are hitting a bug of Firefox. However, as you pointed out, readAsArrayBuffer behaves correctly in every supported browser except Firefox while readAsBinaryString is supported by every browser except IE.
Therefore, it is possible to prefer readAsBinaryString when it exists and fail back to readAsArrayBuffer otherwise.
function readFileAsArrayBuffer(file, success, error) {
var fr = new FileReader();
fr.addEventListener('error', error, false);
if (fr.readAsBinaryString) {
fr.addEventListener('load', function () {
var string = this.resultString != null ? this.resultString : this.result;
var result = new Uint8Array(string.length);
for (var i = 0; i < string.length; i++) {
result[i] = string.charCodeAt(i);
}
success(result.buffer);
}, false);
return fr.readAsBinaryString(file);
} else {
fr.addEventListener('load', function () {
success(this.result);
}, false);
return fr.readAsArrayBuffer(file);
}
}
Usage:
readFileAsArrayBuffer(input.files[0], function(data) {
var array = new Int8Array(data);
output.value = JSON.stringify(array, null, ' ');
window.setTimeout(ReadFile, 1000);
}, function (e) {
console.error(e);
});
Working fiddle: https://jsfiddle.net/Lv5y9m2u/6/
Browser Support:
Firefox: Uses readAsBinaryString, which is not problematic.
IE >= 10: Uses readAsArrayBuffer which is supported.
IE <= 9: The entire FileReader API is not supported.
Almost all other browsers: Uses readAsBinaryString.

addEventlistener function gives error in internet explorer

I have the following code which is used for a mixitup filter, this code regulates the input from an input range and sets it to a checkbox which is checked it works in every browser except for internet explorer (tested in ie11). I think it has something to do with the initial function.
var p = document.getElementById("range"),
res = document.getElementById("output");
p.addEventListener("input", function () {
$("output").html(p.value);
var classes = "";
var minimal = 0;
var maximal = p.value;
$("input[type='range']").attr({'data-filter': "."+ maximal});
$("input[type=checkbox].budget").val('.'+maximal);
$( ".active" ).each(function( index ) {
var thisClass = $(this).attr("data-filter");
if (thisClass == '#mix.activiteiten') {
} else {
if (thisClass != 'undefined') {
classes += thisClass + ',';
}
}
});
if (classes.length > 0) {
var replaced = classes.replace('undefined', '');
var matching = 0;
var arrClasses = replaced.split(",")
}
}, true);
p.addEventListener("change", function() {
var $show = $('#FilterContainer').find('#mix.activiteiten').filter(function(){
var price = Number($(this).attr('data-budget'));
if (classes.length == 0) {
return price >= minimal && price <= maximal;
} else {
for (index = 0; index < arrClasses.length; index++) {
var thisValue = arrClasses[index].replace('.', '');
if ($(this).hasClass(thisValue) && price >= minimal && price <= maximal) {
matching = 1;
return (price >= minimal && price <= maximal);
}
}
}
});
$('#FilterContainer').mixItUp('filter', $show);
}, true);
`
Try this ... by using the jQuery On, you can ensure better response across browsers and versions.
var p = document.getElementById("range"),
res = document.getElementById("output");
$("#range").on("input", function () {
...
}, true);
$("#range").on("change", function() {
...
}, true);
In older IE attachEvent method works instead of addEventListner
see Docs
If you're interested in a cross-browser approach, try creating a function that handles the feature detection for you. Here's one way that might help as a starting point:
function registerEvent( sTargetID, sEventName, fnToBeRun )
{
var oTarget = document.getElementById( sTargetID );
if ( oTarget != null )
{
if ( oTarget.addEventListener ) {
oTarget.addEventListener( sEventName, fnToBeRun, false );
} else {
if ( oTarget.attachEvent )
{
oTarget.attachEvent( sOnEvent, fnToBeRun );
}
}
}
}
Note that this function makes a few assumptions that you may wish to expand in in order to incorporate this into production code, such as error checking, fallback to attribute based event handlers, and so on. Still, it may serve as a proof of concept.
Also, those claiming that IE predominately relies on attachEvent are referring to older versions of IE. Starting with IE9, addEventListener is not only supported, it's recommended for IE. To learn more, see:
How to detect features, rather than browsers
Use feature and behavior detection
IECookbook: Compatibility guidelines and best practices
The IE Blog is a good way to stay up-to-date on the latest news and best practices for IE. (For example, here's the entry talking about why you should use addEventListener instead of attachEvent.)
Hope this helps...
-- Lance
P.S. If 'addEventListener' doesn't seem to be working for you, trying adding <!DOCTYPE html> as the first line of your HTML file. To learn more, see How to enable standards support.
P.P.S. If you create a personal library of such functions, you can greatly reduce the amount of time it takes you to incorporate common tasks into new projects.

node.js wait for a callback/response if i use process.send()

I got a main process and this fork a child. This child do some calculations. At some point of the code in the child I want to ask the parent for some data. This request is highly dynamic and at this point i want to wait for a answer/response of this request. But how do I wait for process.send() or is it possible to add a Callback function to .send()?
I tried to break down my Problem to a simple example.
The highly dynamic value is in my example the randomval in the worker.
And i know that the assignment
var c = process.send({msg:'get_c',randomval:Math.floor((Math.random()*10)+1)});
can't work. But i no other idea how to describe the Problem.
main.js
var childProcessCalc = require('child_process').fork(__dirname + '/worker');
childProcessCalc.send({msgtype:'docalc'});
childProcessCalc.on('message',function(msg){
if(msg.msg === 'pi')
{
console.log("Pi"+msg.pi+" by c="+msg.c);
}
else if(msg.msg === 'get_c')
{
console.log('child wants c');
childProcessCalc.send({msgtype:'your_c',c:1000000*msg.randomval});
}
});
childProcessCalc.on('exit',function(){
console.log('main:the childProzess has exit!')
});
worker.js
process.on('message', function(msg){
if(msg.msgtype == 'docalc') {
//Here is my Problem, how to wait for the message thats the response for
//exactly this send / randomval, or how to add a callback to send
var c = process.send({msg:'get_c',randomval:Math.floor((Math.random()*10)+1)});
var Pi=0;
var n=1;
for (var i=0;i<=c;i++)
{
Pi=Pi+(4/n)-(4/(n+2))
n=n+4
}
process.send({msg:'pi',pi:Pi,c:c})
}
else if(msg.msgtype === 'your_c')
{
console.log('parent hase sendc='+msg.c);
}
});
I have a solution to my problem and it works well for me, but because im very new at nodejs i still not now if this is the best way. Its feel like a overhead.
In a few words what i have done:
I added a object that stores a random callback identifier with the callback function that has to be called if we got a response for the given callback identifier.
When i now call send() from worker i send the identifier to the main process and the main process send this identifier back when he has finished. So i can lookup in my callback var (dynamicMassages ) for the callbackfnc to call and execute it.
main2.js
var childProcessCalc = require('child_process').fork(__dirname + '/worker2');
childProcessCalc.send({msgtype:'docalc'});
childProcessCalc.send({msgtype:'docalc'});
childProcessCalc.on('message',function(msg){
if(msg.msg === 'pi')
{
console.log("Pi"+msg.pi+" by c="+msg.c);
}
else if(msg.msg === 'get_c')
{
console.log('child wants c');
childProcessCalc.send({msgtype:'your_c',callbackid:msg.callbackid, c:1000000*msg.randomval});
}
});
childProcessCalc.on('exit',function(){
console.log('main:the childProzess has exit!')
});
worker2.js
var dynamicMassages = {};
process.on('message', function(msg){
var getRandomId = function(){
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 5; i++ )
{
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
if(dynamicMassages[text] === undefined)
{
return text;
}
else
{
return getRandomId();
}
};
if(msg.msgtype == 'docalc') {
var randomId = getRandomId();
var callbackFnc = function(c){
var Pi=0;
var n=1;
for (var i=0;i<=c;i++)
{
Pi=Pi+(4/n)-(4/(n+2))
n=n+4
}
console.log("callbackFnc For:"+randomId);
process.send({msg:'pi',pi:Pi,c:c})
delete dynamicMassages[randomId];
};
dynamicMassages[randomId] = callbackFnc;//callbackFnc;
process.send({msg:'get_c',callbackid: randomId, randomval:Math.floor((Math.random()*10)+1)});
}
else if(msg.msgtype === 'your_c')
{
console.log('parent hase sendc='+msg.c+' for callbackId '+msg.callbackid);
if(msg.callbackid !== undefined)
{
dynamicMassages[msg.callbackid](msg.c);
}
}
});
Please leave a comment if you would to it the same way.
I'd suggest that you go with a message bus, either a full-blown advanced solution such as RabbitMQ, or a little smaller solution, such as axon.
Basically, what you want to do is inter-process communication, and I'd try to stick to established protocols and standards as much as possible, and avoid rolling your very own solution. As RabbitMQ builds on top of AMQP, I guess you can call it standard.

setInterval only working inside of if(){} block

So I think there is something key to be picked up from this situation I encountered and was hoping some experience could explain it.
When I run this code, it does NOT work:
t5 = "nikolas"+t4;
setInterval(adds,250);
function adds(){
if (t4 < 100){
t4=t4+1;
}
else{
return;
}
};
this does DOES work:
t5 = "nikolas"+t4;
adds(t4);
function adds(a){
if (a < 100){
a=a+1;
setInterval(t4=a,250);
}
else{
return;
}
};
TL;DR: setInterval seems to work inside the if block but not outside. When it works it displays nikolast4 where t4 is an integer that 'ticks' from 1-100 (eg.strong text nikolas0 nikolas1 nikolas2 nikolas3 nikolas4)
Also this code (due to the application I am programming in) is supposed to refresh every 250ms (but take the whole refreshing part with a grain of salt, not totally 100% sure about that).
The code below is fully functionnal, and looks very much like your non-working example.
You can check here :
http://jsbin.com/ofezip/1/edit
So i guess you have an issue with the scope of your variables.
window.onload = function() {
var myOutput = document.createElement("output");
document.body.appendChild(myOutput);
var t4 = 0;
var helloInterval = setInterval(adds,250);
function adds(){
if (t4 < 10){
t4++;
myOutput.value = "hello " + t4;
}
else {
myOutput.value = "goodbye" ;
clearInterval(helloInterval);
return;
}
}
};

Print function log /stack trace for entire program using firebug

Firebug has the ability to log calls to a particular function name. I'm looking for a bug that sometimes stops a page from rendering, but doesn't cause any errors or warnings. The bug only appears about half the time. So how do I get a list of all the function calls for the entire program, or some kind of stack trace for the execution of the entire program?
Firefox provides console.trace() which is very handy to print the call stack. It is also available in Chrome and IE 11.
Alternatively try something like this:
function print_call_stack() {
var stack = new Error().stack;
console.log("PRINTING CALL STACK");
console.log( stack );
}
When i need a stack trace i do the following, maybe you can draw some inspiration from it:
function logStackTrace(levels) {
var callstack = [];
var isCallstackPopulated = false;
try {
i.dont.exist += 0; //doesn't exist- that's the point
} catch (e) {
if (e.stack) { //Firefox / chrome
var lines = e.stack.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
callstack.push(lines[i]);
}
//Remove call to logStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
else if (window.opera && e.message) { //Opera
var lines = e.message.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
var entry = lines[i];
//Append next line also since it has the file info
if (lines[i + 1]) {
entry += " at " + lines[i + 1];
i++;
}
callstack.push(entry);
}
}
//Remove call to logStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
}
if (!isCallstackPopulated) { //IE and Safari
var currentFunction = arguments.callee.caller;
while (currentFunction) {
var fn = currentFunction.toString();
var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf("(")) || "anonymous";
callstack.push(fname);
currentFunction = currentFunction.caller;
}
}
if (levels) {
console.log(callstack.slice(0, levels).join('\n'));
}
else {
console.log(callstack.join('\n'));
}
};
Moderator's note: The code in this answer seems to also appear in this post from Eric Wenderlin's blog. The author of this answer claims it as his own code, though, written prior to the blog post linked here. Just for purposes of good-faith, I've added the link to the post and this note.
I accomplished this without firebug. Tested in both chrome and firefox:
console.error("I'm debugging this code.");
Once your program prints that to the console, you can click the little arrow to it to expand the call stack.
Try stepping through your code one line or one function at a time to determine where it stops working correctly. Or make some reasonable guesses and scatter logging statements through your code.
Try this:
console.trace()
I don't know if it's supported on all browsers, so I would check if it exists first.

Categories

Resources