it-swarm.com.de

Deaktivieren der lang laufenden Skriptnachricht in Internet Explorer

Ich habe eine JavaScript-Funktion, die eine for-Schleife enthält, die so oft durchläuft.
Nach dem Aufruf dieser Funktion zeigt der Browser IE diese Meldung an: 

Dieses Skript anhalten?
Ein Skript auf dieser Seite führt dazu, dass Ihr Webbrowser langsam ausgeführt wird . Wenn es weiter läuft, reagiert Ihr Computer möglicherweise nicht mehr. 

Wie kann ich das beheben?
Gibt es überhaupt eine Möglichkeit, diese Nachricht vom IE aus zu deaktivieren?

49
scatman

Diese Meldung wird angezeigt, wenn Internet Explorer die maximale Anzahl synchroner Anweisungen für ein Stück JavaScript erreicht. Der Standardwert beträgt 5.000.000 Anweisungen. Sie können diese Anzahl auf einem einzelnen Computer erhöhen, indem Sie die Registrierung bearbeiten .

Internet Explorer verfolgt jetzt die Gesamtzahl der ausgeführten Skriptanweisungen und setzt den Wert jedes Mal zurück, wenn eine neue Skriptausführung gestartet wird, z. B. von einem Timeout oder von einem Ereignishandler, für die aktuelle Seite mit der Skript-Engine. Internet Explorer zeigt ein Dialogfeld "Langzeitskript" an, wenn dieser Wert einen Schwellenwert überschreitet.

Die einzige Möglichkeit, das Problem für alle Benutzer zu lösen, die möglicherweise Ihre Seite anzeigen, besteht darin, die Anzahl der Iterationen, die Ihre Schleife mit Timern ausführt, aufzulösen oder Ihren Code so umzuformen, dass er nicht so viele Anweisungen verarbeiten muss.

Das Aufbrechen einer Schleife mit Timern ist relativ unkompliziert:

var i=0;
(function () {
    for (; i < 6000000; i++) {
        /*
            Normal processing here
        */

        // Every 100,000 iterations, take a break
        if ( i > 0 && i % 100000 == 0) {
            // Manually increment `i` because we break
            i++;
            // Set a timer for the next iteration 
            window.setTimeout(arguments.callee);
            break;
        }
    }
})();
80
Andy E

Das nicht reagierende Skriptdialogfeld zeigt an, wenn ein Javascript-Thread zu lange dauert, um vollständig zu sein. Das Bearbeiten der Registrierung könnte funktionieren, aber Sie müssten dies auf allen Client-Computern tun. Sie können einen "rekursiven Abschluss" wie folgt verwenden, um das Problem zu beheben. Es handelt sich lediglich um eine Codierungsstruktur, in der Sie eine lange Vorlaufschleife nehmen und diese in etwas umwandeln können, das einige Arbeit erledigt. Sie verfolgt, wo sie aufgehört hat, gibt dem Browser nach und setzt dort fort, wo es aufgehört hat.

Abbildung 1, Fügen Sie diese Utility-Klasse RepeatingOperation Ihrer Javascript-Datei hinzu. Sie müssen diesen Code nicht ändern:

RepeatingOperation = function(op, yieldEveryIteration) {

  //keeps count of how many times we have run heavytask() 
  //before we need to temporally check back with the browser.
  var count = 0;   

  this.step = function() {

    //Each time we run heavytask(), increment the count. When count
    //is bigger than the yieldEveryIteration limit, pass control back 
    //to browser and instruct the browser to immediately call op() so
    //we can pick up where we left off.  Repeat until we are done.
    if (++count >= yieldEveryIteration) {
      count = 0;

      //pass control back to the browser, and in 1 millisecond, 
      //have the browser call the op() function.  
      setTimeout(function() { op(); }, 1, [])

      //The following return statement halts this thread, it gives 
      //the browser a sigh of relief, your long-running javascript
      //loop has ended (even though technically we havn't yet).
      //The browser decides there is no need to alarm the user of
      //an unresponsive javascript process.
      return;
      }
    op();
  };
};

Abbildung 2, Der folgende Code stellt Ihren Code dar, der das Dialogfeld "Dieses Skript zum Stoppen des Skripts stoppen" verursacht, da der Vorgang so lange dauert:

process10000HeavyTasks = function() {
  var len = 10000;  
  for (var i = len - 1; i >= 0; i--) {
    heavytask();   //heavytask() can be run about 20  times before
                   //an 'unresponsive script' dialog appears.
                   //If heavytask() is run more than 20 times in one
                   //javascript thread, the browser informs the user that
                   //an unresponsive script needs to be dealt with.  

                   //This is where we need to terminate this long running
                   //thread, instruct the browser not to panic on an unresponsive
                   //script, and tell it to call us right back to pick up
                   //where we left off.
  }
}

Figure 3. Der folgende Code ist der Fix für den problematischen Code in Abbildung 2. Beachten Sie, dass die for-Schleife durch einen rekursiven Abschluss ersetzt wird, der alle 10 Iterationen von heavytask () die Kontrolle an den Browser zurückgibt.

process10000HeavyTasks = function() {

  var global_i = 10000; //initialize your 'for loop stepper' (i) here.

  var repeater = new this.RepeatingOperation(function() {

    heavytask();

    if (--global_i >= 0){     //Your for loop conditional goes here.
      repeater.step();        //while we still have items to process,
                              //run the next iteration of the loop.
    }
    else {
       alert("we are done");  //when this line runs, the for loop is complete.
    }
  }, 10);                   //10 means process 10 heavytask(), then
                            //yield back to the browser, and have the
                            //browser call us right back.

  repeater.step();          //this command kicks off the recursive closure.

};

Aus dieser Quelle angepasst:

http://www.picnet.com.au/blogs/Guido/post/2010/03/04/How-to-prevent-Stop-running-this-script-message-in-browsers

12
Eric Leschinski

In meinem Fall musste ich während der Videowiedergabe immer eine Funktion currentTime von Video-Updates aufrufen. Also habe ich timeupdate event of video benutzt und wusste, dass es mindestens 4-mal pro Sekunde ausgelöst wurde (abhängig vom verwendeten Browser, siehe this ). Also habe ich es so geändert, dass jede Sekunde eine Funktion aufgerufen wird:

var currentIntTime = 0;

var someFunction = function() {
    currentIntTime++;
    // Do something here
} 
vidEl.on('timeupdate', function(){
    if(parseInt(vidEl.currentTime) > currentIntTime) {
        someFunction();
    }
});

Dies reduziert die Anzahl der Aufrufe von someFunc um mindestens 1/3 und kann dazu beitragen, dass sich Ihr Browser normal verhält. Es hat für mich getan !!!

1
vinesh

Ich kann die vorherigen Antworten nicht kommentieren, da ich sie nicht ausprobiert habe. Ich weiß jedoch, dass die folgende Strategie für mich funktioniert. Es ist etwas weniger elegant, aber erledigt die Arbeit. Es ist auch nicht erforderlich, Code in Brocken zu brechen, wie es bei anderen Ansätzen der Fall ist. In meinem Fall war dies keine Option, da mein Code rekursive Aufrufe der Logik hatte, die gerade geloopt wurde. d. h., es gab keine praktische Möglichkeit, einfach aus der Schleife zu springen und dann in gewisser Weise fortfahren zu können, indem globale Variablen verwendet werden, um den aktuellen Status beizubehalten, da diese Globals durch Verweise auf sie in einem nachfolgenden rekursiven Aufruf geändert werden könnten. Ich brauchte also einen einfachen Weg, der dem Code keine Chance bietet, die Integrität des Datenzustands zu beeinträchtigen.

Angenommen, "Skript stoppen?" Das Dialogfeld wird während einer for () - Schleifenausführung nach einer Reihe von Iterationen (in meinem Fall etwa 8-10) angezeigt. Das Durcheinander mit der Registrierung ist keine Option. Hier war der Fix (für mich sowieso):

var anarray = [];
var array_member = null;
var counter = 0; // Could also be initialized to the max desired value you want, if
                 // planning on counting downward.

function func_a()
{
 // some code
 // optionally, set 'counter' to some desired value.
 ...
 anarray = { populate array with objects to be processed that would have been
             processed by a for() }
 // 'anarry' is going to be reduced in size iteratively.  Therefore, if you need
 //  to maintain an orig. copy of it, create one, something like 'anarraycopy'.
 //  If you need only a shallow copy, use 'anarraycopy = anarray.slice(0);'
 //  A deep copy, depending on what kind of objects you have in the array, may be
 //  necessary.  The strategy for a deep copy will vary and is not discussed here.
 //  If you need merely to record the array's orig. size, set a local or
 //  global var equal to 'anarray.length;', depending on your needs.
 // - or -
 // plan to use 'counter' as if it was 'i' in a for(), as in
 // for(i=0; i < x; i++ {...}

   ...

   // Using 50 for example only.  Could be 100, etc. Good practice is to pick something
   // other than 0 due to Javascript engine processing; a 0 value is all but useless
   // since it takes time for Javascript to do anything. 50 seems to be good value to
   // use. It could be though that what value to use does  depend on how much time it
   // takes the code in func_c() to execute, so some profiling and knowing what the 
   // most likely deployed user base is going to be using might help. At the same 
   // time, this may make no difference.  Not entirely sure myself.  Also, 
   // using "'func_b()'" instead of just "func_b()" is critical.  I've found that the
   // callback will not occur unless you have the function in single-quotes.

   setTimeout('func_b()', 50);

  //  No more code after this.  function func_a() is now done.  It's important not to
  //  put any more code in after this point since setTimeout() does not act like
  //  Thread.sleep() in Java.  Processing just continues, and that is the problem
  //  you're trying to get around.

} // func_a()


function func_b()
{
 if( anarray.length == 0 )
 {
   // possibly do something here, relevant to your purposes
   return;
 }
//  -or- 
if( counter == x ) // 'x' is some value you want to go to.  It'll likely either
                   // be 0 (when counting down) or the max desired value you
                   // have for x if counting upward.
{
  // possibly do something here, relevant to your purposes
  return;
}

array_member = anarray[0];
anarray.splice(0,1); // Reduces 'anarray' by one member, the one at anarray[0].
                     // The one that was at anarray[1] is now at
                     // anarray[0] so will be used at the next iteration of func_b().

func_c();

setTimeout('func_b()', 50);

} // func_b()


function func_c()
{
  counter++; // If not using 'anarray'.  Possibly you would use
             // 'counter--' if you set 'counter' to the highest value
             // desired and are working your way backwards.

  // Here is where you have the code that would have been executed
  // in the for() loop.  Breaking out of it or doing a 'continue'
  // equivalent can be done with using 'return;' or canceling 
  // processing entirely can be done by setting a global var
  // to indicate the process is cancelled, then doing a 'return;', as in
  // 'bCancelOut = true; return;'.  Then in func_b() you would be evaluating
  // bCancelOut at the top to see if it was true.  If so, you'd just exit from
  // func_b() with a 'return;'

} // func_c()
0
Matt Campbell