Jump to content

Extend IE's Script Timeout


ShadowMage

Recommended Posts

I have a script that takes a few seconds to run. There is a lot of code that needs to be run. Firefox does not timeout when it runs but IE does. By timeout I mean I get an error saying that the script is causing Internet Exploder...I mean Explorer...to run slowly, do you want to stop the script.How can I stop that from happening? I read about a Windows registry change that would change how many lines of code are executed before the message pops up but that's hardly a good solution. I also don't want my users to have to click 'Continue' three times to run the script.Is there a way to fool IE into not producing that message? Or can you give me pointers on how to optimize my code? (I know the second question will be hard without seeing my code, but general pointers will be good)

Link to comment
Share on other sites

i have an idea that would involve passing off states to other timers and then kill the previous timer, and then to another, etc until its been broken down enough into scripts that Exploder doesn't complain; but that's certainly the tricky way of doing it, and may not fit your situation, depending on the nature of the code being executed.but hopefully there's a much easier solution, hehe

Link to comment
Share on other sites

i have an idea that would involve passing off states to other timers and then kill the previous timer, and then to another, etc until its been broken down enough into scripts that Exploder doesn't complain; but that's certainly the tricky way of doing it, and may not fit your situation, depending on the nature of the code being executed.
What do you mean by the 'nature of the code'? Whether I'm using loops and nested loops or a lot of function calls, or something entirely different?
Link to comment
Share on other sites

i guess it depends now that I'm thinking about it. For instance, say your script takes 10 seconds to run, but IE freaks out after 4 seconds. After second 3, you would start a new timer to pick up where the current function (say onLoad) ended. You could automate this by having a timer that counts to 3 (onLoad), and when it reaches three, start the new timer, for seconds 4-6. Then repeat for seconds 7-9, and by second 10 everything should be loaded, and you can clear all timers. Like I said, where the next timer picks up has to be where the last one ends, so if you're not executing some sort of script that's creating some sort of state structure, where if the second function started from "scratch" it wouldn't work (hence you would pass the current state from one timer to the next, so it could piggy back off the last one) then you probably don't have to worry. If its just a matter of executing code line-by-line, and the current line has little to do with the previous line, then you can just block segment your code into separate functions and use the triggering timer methods I'm rambling on about, haha.Or, what if you made your script an AJAX call but set it's async to be false (in the send request)? So it won't wait for response? Not sure if that would still count as the current script running though and still bomb out Exploderton McExplodingting.

Link to comment
Share on other sites

Wow....Umm...Let's start with the state structure you mention. What exactly is that? I've only just begun to sink my teeth into JavaScript (well programming in general really) so when you start rambling about the state structure and other more in depth stuff, I really have no idea what you're talking about. All my script is doing is running a bunch of calculations using values of elements on the page and then updating other elements accordingly. I do have a complex JSON object that I construct and use in a lot of the calculations.Secondly, by timer you mean with setTimeout()?

Link to comment
Share on other sites

Is it possible to see your script? Might give me a better idea of way's to consolidate your code. Basically the idea is to try and break down the entire script into smaller executable chunks, so that way one script doesn't hog all the time, causing your IE error. However, with calculations and whatnot, you can't miss any steps along the way, and this order of steps is what determines how your page will ultimately be displayed (its state). So we have to make sure the functions don't change too soon. If you were just executing a ton of document.write statements, you wouldn't have to worry too much about them. Put the first 10 in a function, then the second 10 in a function, and so and so forth, to be beat the clock as it were. However, if one part of your function determines part of how your site displays, and then another part acts on that, if we split them up into separate functions, then we need to preserve the state at that time, and pass it along so we can keep working. for example, say your function does this:if one part calculates a variable z:z = x + y;and then another part uses z for something in its calculation, in this case q:q = r % z;unless you use globals (not recommended as per best practices), and you break these lines of code up into two separate functions (to beat the clock), you need to pass z to the next function. (maybe in this case z would be part of the state of your page as the script is executing, based on the users current browser window size. not necessarily the whole state of the page, but still part of what will ultimately determine, say the width of a div based on the width of another one (which q finds out), or maybe if q is equal to 12 in function 2 (based on a calculation involving z from part one), then set a special class for the overflow property of another one).it all depends on the flow of your script, and how these calculations relate to one another. I may be making this more complex for such a seemingly simple problem, which is why I was hoping there was some sort of property somewhere to handle this that someone may know of, but if not, this might be the only way to keep IE from stinking donkey doo-doo.

Link to comment
Share on other sites

Well, I guess I can give you portions of the code. If I were to paste it all in here this post would be enormous (there's nearly 3000 lines of code in total). So here is the function that is called when certain elements are changed on the page.

function refreshPage() {	var TotalCost = 0;		setSlopeDims();	refreshFiberglass(false);		arrParts = calcFramePieces();		setElement("FrameWght", FormatNumber(getFrameWeight(), 1));	setElement("GlazeWght", FormatNumber(getPanelWeight(), 1));		var types = 1;	var sqFt = 0;	var tmpSqFt = 0;	while (document.getElementById("Quantity"+types)) {		tmpSqFt = getSqFeet(types);		setElement("SqFtType"+types, FormatNumber(tmpSqFt, 1));		sqFt += tmpSqFt;		types++;	}	setElement("SqFt", FormatNumber(sqFt, 1));	setElement("MtlCost", FormatNumber(calcMetalCost(), 2));		refreshFinish(false);	refreshMisc(false);	refreshBrMtl(false);	refreshEngHours(false, false);		setElement("PnlHrs", FormatNumber(calcPanelHours(), 0));	setElement("PLaborCost", FormatNumber(calcPnlHrsCost(), 2));	setElement("FrameHrs", FormatNumber(calcFrameHours(), 0));	setElement("FLaborCost", FormatNumber(calcFrameHrsCost(), 2));		refreshAssmHours(false);		setTotalInformation();		refreshPiecesTable()}

This function is relatively short as well as most of the refresh functions. But those functions call other functions, some of which are very large. calcFramePieces is one such large function with nearly 300 lines. A good chunk of my functions have at least one loop, and some have loops nested two or three deep.The calcFramePieces function would be one that relies on the two before it. Otherwise everything else pretty much relies on calcFramePieces.BTW, I appreciate you taking the time to help me with this! :)Edit: Oh and it doesn't time out until it gets to the refreshPiecesTable() if that helps any. I'm not sure if it's counting all the script before it or not. From what you are saying it sounds like the refreshPiecesTable() is the one that is timing out. I'll test that and get back.Edit2: If I comment everything but refreshPiecesTable() it runs just fine. So apparently my understanding of what you told me is a little off.

Link to comment
Share on other sites

no problem, hopefully we can figure something out for you, although timers will most certainly always complicate matters, so hopefully we can get around them. :) maybe you could link me a page with the whole script in it, or a test page we can test in Firebug with?

Link to comment
Share on other sites

It's difficult to try and optimize code like this without knowing exactly what is taking up so much time. I would recommend loading your site in Firefox using Firebug, and using Firebug's Profile tool to figure out what is taking a while to run. If you have Firebug open, on the Console tab near the top you'll see a button marked "Profile". If you click that button, you will start profiling. Clicking the button a second time will stop profiling. Load your site up, and before the heavy code starts hit the profile button, and then when everything is done hit the profile button again to stop it. You'll end up with a report about all of the functions that were executed during the profile, how many times each was executed, and the total time each took. That will be a good starting point to figure out what you need to focus your effort on.

Link to comment
Share on other sites

Alright, I'll give that a shot. I'll look at that and see if I can work out a better way to do what I need to do. In the meantime, if I'm unable to optimize it much further, I'm still open to suggestions on how to fool IE into running it.Thanks.

Link to comment
Share on other sites

Well after running this through the profiler in Firebug it leads me to my DecimalToFraction function. This function is called 100 times (in comparison to 379 or 550 times for some other functions) and chews up 96% of the time.I'm not sure how I can optimize this though. Here's the function:

function DecimalToFraction(tmpDecimal, fraction) {	//Function inspired by script posted by AdamBrill on this forum:	//http://www.webdeveloper.com/forum/archive/index.php/t-18205.html	if (fraction !== undefined) {		tmpDecimal = tmpDecimal.toFraction(fraction);	}		var strDecimal = "";	var arrNumParts = "";	var wholeNum = 0;	var decimal = 0;	var num = "1";	var tmpFraction = "";		if (!isNaN(tmpDecimal)) {		strDecimal = tmpDecimal.toString();				arrNumParts = strDecimal.split('.');		wholeNum = arrNumParts[0];		tmpFraction = wholeNum;				if (arrNumParts[1]) {			decimal = parseFloat("."+arrNumParts[1].substr(0, 5));						var bound = strDecimal.length-2;			for(z=0; z<bound; z++) {				num += "0";			}						num = parseInt(num);			decimal = parseInt(decimal*num);						bound = decimal+1;			for(z=2; z<bound; z++) {				if(decimal%z==0 && num%z==0) {					decimal = decimal/z;					num = num/z;					z=1;				}			}						if (wholeNum != 0) {				tmpFraction += " ";			} else {				tmpFraction = "";			}						tmpFraction += decimal+"/"+num		}	}		return tmpFraction;} //End function DecimalToFraction

Any suggestions?

Link to comment
Share on other sites

There are some minor things you could do to speed that up, but converting from a decimal to a fraction is just an expensive task, there's a lot of trial and error involved. I'm not sure if that algorithm is the most efficient one to use, but it's looping potentially thousands or millions of times per conversion, depending on how big the number is. It's probably possible to stop the second loop sooner than letting it run. Analyze that second loop, pay attention to how many times it loops and also notice that the z counter gets reset inside the loop. If the bound variable is 10000, and it finds 10 numbers that evenly divide into the numerator and denominator, then it's going to run that loop 100000 times. In addition to resetting z inside that loop, you can probably also reset bound. Analyze how that loop works and you'll probably be able to identify a way to decrease the number of times it needs to run.

Link to comment
Share on other sites

Thanks for the advice. I played around with my algorithm and came up with what I thought should be a more efficient one. Here is the new function:

function DecimalToFraction(tmpDecimal, fraction) {	if (fraction !== undefined) {		tmpDecimal = tmpDecimal.toFraction(fraction);	}		var strDecimal = "";	var arrNumParts = "";	var wholeNum = 0;	var decimal = 0;	var num = "1";	var tmpFraction = "";		if (!isNaN(tmpDecimal)) {		strDecimal = tmpDecimal.toString();				arrNumParts = strDecimal.split('.');		wholeNum = arrNumParts[0];		tmpFraction = wholeNum;				if (arrNumParts[1]) {			decimal = parseFloat("."+arrNumParts[1].substr(0, 5));						num = Math.pow(10, strDecimal.length-2);			decimal = parseInt(decimal*num);						bound = decimal+1;			var blnDone = false;			for (i=2; i < bound; i++) {				while ((decimal%i == 0) && (num%i == 0))				{					decimal = decimal/i;					num = num/i;					if (i > decimal) {						blnDone = true;						break;					}				}				if (blnDone) {					break;				}			}						if (wholeNum != 0) {				tmpFraction += " ";			} else {				tmpFraction = "";			}						tmpFraction += decimal+"/"+num		}	}		return tmpFraction;} //End function DecimalToFraction

Unfortunately, this has made it less efficient?! It takes longer to run than it did before. In IE I get the error message four times instead of three. Is my new function really that much less efficient?

Link to comment
Share on other sites

hey jkloth, I ran this scenario by someone here at work and they built upon one of my initial ideas of using AJAX. There idea being you have this function run on the server (if that's possible in your case) and you can use jQuery's AJAX API to get values back.http://api.jquery.com/category/ajax/

Link to comment
Share on other sites

The problem is that it's looping however many times the input is. If the number to convert is 0.12345, then num is 100,000, decimal is 12,345, and bound is 12,346. So it's looping over 12,000 times for the main loop, and inside the main loop is another while loop. So it runs that while loop over 12,000 times, and the while loop may run several times also. So the number of iterations (bound) is increasing at an exponential rate as the decimal gets larger. In computer science speak, that means your algorithm runs in O2 time. Exponential time is sort of the worst type of algorithm, just because for larger data it takes an extremely long time.To start with, even though it's still exponential, the value of bound is twice as large as it needs to be. There's never going to be a number larger than half that is going to evenly divide, because the smallest divisor is 2 therefore the largest dividend is half.You may even be able to set bound to a quarter of the value. If you run through (bound/4) possibilities, everything in the second quarter is going to match what you already found in the first quarter. That is, there's never a number that is more than a quarter which will evenly divide into the number which you wouldn't have also found a multiplier for that is less than a quarter. If if evenly divides and the value is more than a quater, then the value it divides by is less than a quarter.You can use 100 as an example. You only need to loop through 1-25 to check for multiples. There is no multiple 26-50 that you wouldn't also find < 25, and there are no multiples at all > 50.

Link to comment
Share on other sites

hey jkloth, I ran this scenario by someone here at work and they built upon one of my initial ideas of using AJAX. There idea being you have this function run on the server (if that's possible in your case) and you can use jQuery's AJAX API to get values back.http://api.jquery.com/category/ajax/
Thank you for that link. I can definitely do this on the server. In fact I already have most of the hardcore calculation functions that I need written in PHP. I'm going to have to research this jQuery library and see if I can figure it out.
The problem is that it's looping however many times the input is. If the number to convert is 0.12345, then num is 100,000, decimal is 12,345, and bound is 12,346. So it's looping over 12,000 times for the main loop, and inside the main loop is another while loop. So it runs that while loop over 12,000 times, and the while loop may run several times also. So the number of iterations (bound) is increasing at an exponential rate as the decimal gets larger. In computer science speak, that means your algorithm runs in O2 time. Exponential time is sort of the worst type of algorithm, just because for larger data it takes an extremely long time.
Right. That's why I have the break statements in there if i becomes greater than the numerator. That should break the loops so that they only run until the numerator is as small as it can get.
To start with, even though it's still exponential, the value of bound is twice as large as it needs to be. There's never going to be a number larger than half that is going to evenly divide, because the smallest divisor is 2 therefore the largest dividend is half.
Again, you're right. I added that in there and it did reduce the time it ran, but I didn't think I needed that extra calculation since that's what the conditional inside the while loop is intended to counter.I do understand the math that is required for this operation, it's putting it into efficient code that I have trouble with.
Link to comment
Share on other sites

that's what the conditional inside the while loop is intended to counter.
Right, but it's only going to set that variable, and break out of the for loop early, if decimal is divisible by i, num is divisible by i, and i is greater than decimal/i. But the for loop is still set to run 4 times as long as it needs to, so you're just hoping that the condition becomes true at some point. In other words, it doesn't make sense to tell the for loop to run 4 times as long as you know it will always need to, and then rely on something else to stop it from actually running that long.
Link to comment
Share on other sites

OK, I see what you're saying now. I will make that change.That still doesn't really explain why it cuts down the time by such a drastic amount. At least for my script that I'm using here, that is. All numbers are rounded to the nearest 1/16 before they're passed to the function so the loop should never run more than 15 times. By rounding the numbers, that should always create a situation where the conditional will be met before i reaches 15, thus setting blnDone and breaking the loop. Am I right?Edit:Ok, I got it I just added an 'or' condition to the second test in the loop so it now looks like this:

for (i=2; i < bound; i++) {	while ((decimal%i == 0) && (num%i == 0))	{		whileTest = true;		test++;		decimal = decimal/i;		num = num/i;		if (i > decimal) {			blnDone = true;			break;		}	}	if ((blnDone) || (i > decimal)) {		break;	}}

I'll get back tomorrow to let you know the result, as I haven't tested yet.

Link to comment
Share on other sites

What about just doing this:for (i=2; i < (bound / 4); i++) {

By rounding the numbers, that should always create a situation where the conditional will be met before i reaches 15, thus setting blnDone and breaking the loop. Am I right?
I'm not sure, I guess it would be worth testing.
Link to comment
Share on other sites

What about just doing this:for (i=2; i < (bound / 4); i++) {
Yep, I did that. Well, not quite like that. I set bound equal to (decimal+1)/4 rather than dividing bound in the loop declaration, but it accomplishes the same thing. :)
I'm not sure, I guess it would be worth testing.
I did some testing and I found out that it did not work as I expected. It would not always reach the conditional in the while loop to test if i was greater than decimal thus not always breaking the loop. By adding the 'or' condition to the other conditional outside the while loop it will always break the loop when decimal has been reduced to its smallest value.This cut the time significantly, from nearly 2000 ms down to just over 1 ms. :)Thanks guys, for all your help! Really appreciate it! :)
Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...