以下代码执行无提示逻辑错误:
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
可能的行为:
第二个for循环后的变量avg
应加倍,数组的长度应为1.14亿。
我应该收到内存错误。
作为脚本运行时的输出:
avg
在第二个for循环后不会更改。答案 0 :(得分:35)
当你在Codepen中编写代码时 - 他们实际上并没有按原样执行它,而是首先对它应用一些转换。
他们将其解析为abstract syntax tree,查找循环和insert instructions explicitly以便在过多时间后停止执行循环。
当你这样做时:
for(let i = 0; i < 114000000; i++ ){
arr.push(new Point());
avg += arr[i].x / 1000;
}
您的代码运行方式为:
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++;
}
您可以通过检查CodePen内部的框架代码来看到这一点。
他们在您的代码中注入shouldStopLoop
次调用。
他们有一个名为stopExecutionOnTimeout
的脚本,它做了类似的事情(源自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.";
};
如果你想自己运行它 - JSBin具有类似的功能,它们有open sourced it作为循环保护库 - 低于500 LoC。
答案 1 :(得分:12)