web-dev-qa-db-fra.com

Désactivation du message de script de longue durée dans Internet Explorer

J'ai une fonction JavaScript qui contient une boucle for qui itère tant de fois.
Après avoir appelé cette fonction, le navigateur IE affiche le message suivant: 

N'exécutez plus ce script?
Un script sur cette page provoque l’exécution lente de votre navigateur Web . S'il continue à fonctionner, votre ordinateur risque de ne plus répondre. 

Comment puis-je réparer cela?
y at-il de toute façon je peux désactiver ce message depuis IE?

49
scatman

Ce message s'affiche lorsque Internet Explorer atteint le nombre maximal d'instructions synchrones pour un élément de JavaScript. Le nombre maximum d'instructions par défaut est de 5 000 000. Vous pouvez augmenter ce nombre sur un seul ordinateur en modifiant le registre .

Internet Explorer suit maintenant le nombre total d'instructions de script exécutées et réinitialise la valeur chaque fois qu'une nouvelle exécution de script est lancée, par exemple à partir d'un délai ou d'un gestionnaire d'événements, pour la page en cours avec le moteur de script. Internet Explorer affiche une boîte de dialogue "script de longue durée" lorsque cette valeur dépasse un seuil.

Le seul moyen de résoudre le problème pour tous les utilisateurs susceptibles d'afficher votre page consiste à diviser le nombre d'itérations effectuées par votre boucle à l'aide de minuteries ou à refactoriser votre code afin qu'il n'ait pas besoin de traiter autant d'instructions.

Casser une boucle avec des timers est relativement simple:

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

La boîte de dialogue de script ne répondant pas indique quand un fil JavaScript prend trop de temps. La modification du registre peut fonctionner, mais vous devez le faire sur tous les ordinateurs clients. Vous pouvez utiliser une "fermeture récursive" comme suit pour atténuer le problème. Il s’agit simplement d’une structure de codage dans laquelle vous pouvez utiliser une longue boucle et la transformer en quelque chose qui fonctionne, et qui garde la trace de celle-ci, cédant au navigateur, puis continuant là où elle s’est arrêtée jusqu’à ce que nous ayons terminé.

Figure 1, Ajoutez cette classe d’utilitaires RepeatingOperation à votre fichier javascript. Vous n'aurez pas besoin de changer ce code:

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();
  };
};

Figure 2, Le code suivant représente votre code à l'origine de la boîte de dialogue "Arrêter l'exécution de ce script" car sa réalisation est très longue:

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. Le code suivant est le correctif du code problématique de la figure 2. Notez que la boucle for est remplacée par une fermeture récursive qui redonne le contrôle au navigateur toutes les 10 itérations de heavytask ()

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.

};

Adapté de cette source:

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

12
Eric Leschinski

Dans mon cas, lors de la lecture d'une vidéo, j'avais besoin d'appeler une fonction à chaque fois currentTime de mises à jour vidéo. J'ai donc utilisé timeupdate événement vidéo et j'ai appris qu'il était déclenché au moins 4 fois par seconde (cela dépend du navigateur que vous utilisez, voir this ). Je l'ai donc changé pour appeler une fonction chaque seconde comme ceci:

var currentIntTime = 0;

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

Cela réduit les appels à someFunc d'au moins 1/3 et peut aider votre navigateur à se comporter normalement. C'est fait pour moi !!!

1
vinesh

Je ne peux pas commenter les réponses précédentes car je ne les ai pas essayées. Cependant, je sais que la stratégie suivante fonctionne pour moi. C'est un peu moins élégant mais fait le travail. De plus, il n’est pas nécessaire de diviser le code en morceaux comme le font d’autres approches. Dans mon cas, ce n'était pas une option, car mon code comportait des appels récursifs à la logique en cours de boucle. c’est-à-dire qu’il n’existait aucun moyen pratique de sortir de la boucle, puis de reprendre d’une manière ou d’une autre en utilisant des vars globaux pour préserver l’état actuel, car ces globaux pourraient être modifiés par des références à ces appels lors d’un appel récursif ultérieur. J'avais donc besoin d'une solution simple qui n'offrirait aucune chance au code de compromettre l'intégrité de l'état des données.

En supposant que le "script d'arrêt?" La boîte de dialogue apparaît pendant l'exécution d'une boucle for () après un certain nombre d'itérations (dans mon cas, environ 8 à 10), et déconner avec le registre n'est pas une option, voici le correctif (pour moi, en tout cas):

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