The following code performs a silent logical error:
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 behaviour(s):
The variable avg
after the second for loop should be doubled and The length of array should be 114 million.
I should get a memory error.
Output when run as a script:
avg
Does not change after the second for loop.When you write code in Codepen - they actually don't execute it as-is but rather first apply some transformations to it.
They parse it into an abstract syntax tree, find loops and insert instructions explicitly to stop executing the loop if too much time has passed.
When you do:
for(let i = 0; i < 114000000; i++ ){ arr.push(new Point()); avg += arr[i].x / 1000; }
Your code runs as:
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++; }
You can see this by inspecting the frame code inside CodePen itself.
They inject shouldStopLoop
calls inside your code. They have a script called stopExecutionOnTimeout
which does something like this (source from 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]."; };
If you want to run it yourself - JSBin has similar functionality and they have open sourced it as the loop-protect library - under 500 LoC.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With