Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript IOS5 "JavaScript execution exceeded timeout"

the following test is basically ~1000 math operations and works fine on most PC and android browsers, and iOS 4.x. On iOS5 safari (iPhone 4 and iPad 2) we get "JavaScript: Error undefined JavaScript execution exceeded timeout". Any help greatly appreciated thanks.

/** Converts numeric degrees to radians */
if (typeof (Number.prototype.toRad) === "undefined") {
    Number.prototype.toRad = function () {
    return this * Math.PI / 180;
    }
}

function gc(lat1, lon1, lat2, lon2) {
    // returns the distance in km between a pair of latitude and longitudes
    var R = 6371; // km
    var dLat = (lat2 - lat1).toRad();
    var dLon = (lon2 - lon1).toRad();
    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
        Math.sin(dLon / 2) * Math.sin(dLon / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = R * c;
    return d;
}

function test() {
    var d1 = new Date();
    var lat1, lon1, lat2, lon2;
    lat1 = -36;
    lon1 = 174;

    lat2 = lat1;
    lon2 = lon1;

    while (lat2 > -37) {
    lat2 = lat2 - 0.001;
    var stest = "lat1=" + lat1 + ",lon1=" + lon1 + ",lat2=" + lat2 + ",lon2=" + lon2 + "=" + gc(lat1, lon1, lat2, lon2);

    }
    var d2 = new Date();
    var stest = (d2.getTime() - d1.getTime()) / 1000.0 + "s";
    $("#lblTest").html(stest + "<BR/>" + $("#lblTest").html());

}
like image 844
ajayel Avatar asked Oct 16 '11 20:10

ajayel


1 Answers

If you want to execute a long running operation in javascript and you are getting anywhere close to the script execution time limit enforced by some browsers, then you will have to break your function into multiple pieces, run one piece, to a very short setTimeout(fn, 1) and then execute the next piece, etc... Doing it this way, you can run code for hours because it gives other scripts and other events a chance to process. It sometimes requires a minor amount of code restructuring to be able to do this, but it's always possible with a little work.

The basic concept in pseudo-code would be this:

var state = {};   // set initial state
var done = false;

function doWork() {
   // do one increment of work that will never get even close to the browser
   // execution time limit
   // update the state object with our current operating state for the next execution
   // set done = true when we're done processing
   if (!done) {
       setTimeout(doWork, 1);
   }
}

doWork();

In your specific code, you could do something like this. You can process 100 latitude points at a time and then do a short setTimeout to do the next 100 and so on. You can adjust that 100 number to whatever would work best. The higher the number, the more you do on each timer and the better overall execution time, but the closer you get to the browser script execution limit. The setTimeout keeps the browser alive (processing other events) and prevents the execution time limit from kicking in.

function test() {
    var d1 = new Date();
    var lat1, lon1, lat2, lon2, done = false;;
    lat1 = -36;
    lon1 = 174;

    lat2 = lat1;
    lon2 = lon1;

    function calcGC() {
        var cntr = 0;
        while (lat2 > -37 && cntr < 100) {
            lat2 = lat2 - 0.001;
            var stest = "lat1=" + lat1 + ",lon1=" + lon1 + ",lat2=" + lat2 + ",lon2=" + lon2 + "=" + gc(lat1, lon1, lat2, lon2);
            cntr++;
        }
        // if we have more to go, then call it again on a timeout
        if (lat2 > -37) {
            setTimeout(calcGC, 1);
        } else {
            var d2 = new Date();
            var stest = (d2.getTime() - d1.getTime()) / 1000.0 + "s";
            $("#lblTest").html(stest + "<BR/>" + $("#lblTest").html());
        }
    }
    calcGC();
}
like image 135
jfriend00 Avatar answered Nov 06 '22 18:11

jfriend00