Podczas pisania kodu w Codepen - faktycznie nie wykonać go jak jest, lecz najpierw zastosować pewne przekształcenia do niego.
Parse to w abstract syntax tree, znajdź pętle i insert instructions explicitly, aby zatrzymać wykonywanie pętli, jeśli minęło zbyt dużo czasu.
Kiedy zrobić:
for(let i = 0; i < 114000000; i++){
arr.push(new Point());
avg += arr[i].x/1000;
}
Twój kod działa jako:
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++;
}
Można zobaczyć, sprawdzając kod ramki wewnątrz samego CodePen.
Wstrzykują one połączenia shouldStopLoop
w swoim kodzie. Mają skrypt o nazwie stopExecutionOnTimeout
który robi coś takiego (źródło z 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 [email protected]";
};
Jeśli chcesz uruchomić go samodzielnie - JSBin posiada podobną funkcjonalność i mają open sourced it jak biblioteki pętli-Protect - poniżej 500 LoC.
Mała nuta: "średnia" nie zmienia się z powodu precyzji liczb zmiennoprzecinkowych. W pewnym momencie wartość 'avg' staje się tak duża, że dodanie do niej niewielkiej liczby nie ma żadnego wpływu ze względu na rozmiar bitowy mantysy. –
może być problem z 'i <114000000'? [codepen bez żadnych tablic] (http://codepen.io/vpArth/pen/eZeGGr) –
@AndersTornblad 'Math.random() * 1000000/1000' powinno być w połowie przypadków większe niż 500, co zdecydowanie nie jest wystarczające do połknięcia przez zaokrąglenie błędów/precyzji dla tak małej liczby jak '1261167461.290721' (dla mojego bieżącego uruchomienia) – zerkms