27 votes

Comportement étrange de JavaScript dans CodePen avec des tableaux énormes

Le code suivant effectue en silence une erreur de logique:

const arr = [];
class Point{
  constructor(){
    this.x = Math.random() * 1000000;
    this.y = Math.random() * 1000000;
  }
}
console.time('foo');
let avg = 0;

for(let i = 0; i < 114000000; i++ ){
  arr.push(new Point());
  avg += arr[i].x / 1000;
}
console.log(avg, arr.length);

// shouldn't this double the avg ?
for(let i = 0; i < 114000000; i++ ){
  avg += arr[i].x / 1000;
}

console.log(avg, arr.length);
console.timeEnd('foo');

CodePen - http://codepen.io/darkyen/pen/yOPMZg?editors=0010

Possible le comportement(s):

  • La variable avg après la deuxième boucle for doit être doublé et La longueur de tableau doit être de 114 millions de dollars.

  • Je devrais obtenir une erreur de mémoire.

De sortie lorsque vous exécutez un script:

  • avg Ne change pas après la deuxième boucle for.
  • Longueur du tableau n'est pas 114 Mil, (Chrome 2-3M, Firefox Dev 5 Mil, MS Bord 788k).

34voto

Benjamin Gruenbaum Points 51406

Lorsque vous écrivez du code dans Codepen - en fait ils n'exécutent pas comme ça mais plutôt d'abord appliquer un peu de transformations.

Ils analyser dans un arbre de syntaxe abstraite, trouver des boucles et insérer des instructions explicitement à l'arrêt de l'exécution de la boucle si trop de temps s'est écoulé.

Lorsque vous faites:

for(let i = 0; i < 114000000; i++ ){
  arr.push(new Point());
  avg += arr[i].x / 1000;
}

Votre code s'exécute en tant que:

for (var i = 0; i < 114000000; i++) {
    if (window.CP.shouldStopExecution(1)) { // <- injected by Codepen!!!
        break;
    }
    arr.push(new Point());
    avg += arr[i].x / 1000;
    iter++;
}

Vous pouvez le voir par l'inspection de l'image de code à l'intérieur de CodePen lui-même.

Ils injectent shouldStopLoop des appels à l'intérieur de votre code. Ils ont un script qui s'appelle stopExecutionOnTimeout qui fait quelque chose comme ceci (source de Codepen):

 var PenTimer {
   programNoLongerBeingMonitored:false,
   timeOfFirstCallToShouldStopLoop:0, // measure time
   _loopExits:{}, // keep track of leaving loops
   _loopTimers:{}, // time loops
   START_MONITORING_AFTER:2e3, // give the script some time to bootstrap
   STOP_ALL_MONITORING_TIMEOUT:5e3, // don't monitor after some time
   MAX_TIME_IN_LOOP_WO_EXIT:2200, // kill loops over 2200 ms
   exitedLoop:function(o) { // we exited a loop 
     this._loopExits[o] = false; // mark
   },
   shouldStopLoop:function(o) { // the important one, called in loops
      if(this.programKilledSoStopMonitoring)  return false; // already done
      if(this.programNoLongerBeingMonitored)return true;
      if(this._loopExits[o])  return false; 
      var t=this._getTime(); // get current time
      if(this.timeOfFirstCallToShouldStopLoop === false) 
        this.timeOfFirstCallToShouldStopLoop = t;
        return false;
      }
      var i= t - this.timeOfFirstCallToShouldStopLoop; // check time passed
      if(i<this.START_MONITORING_AFTER) return false; // still good   
      if(i>this.STOP_ALL_MONITORING_TIMEOUT){
        this.programNoLongerBeingMonitored = true;
        return false;
      }
      try{
        this._checkOnInfiniteLoop(o,t);
      } catch(n) {
        this._sendErrorMessageToEditor(); // send error about loop
        this.programKilledSoStopMonitoring=false;
        return true; // killed
      }
      return false; // no need
   },
   _sendErrorMessageToEditor:function(){/*... */
      throw "We found an infinite loop in your Pen. We've stopped the Pen from running. Please correct it or contact support@codepen.io.";
};

Si vous voulez l'exécuter vous-même - JSBin a des fonctionnalités similaires et ils ont open source il que la boucle-protéger de la bibliothèque - moins de 500 LoC.

11voto

vp_arth Points 1241

Ce ne sont que codepen restrictions de lancement de script.

J'exécute le script en Chrome Developer Tools et en Node.JS REPL - tout semble ok.


Codepen docs

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by: