最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Strange JavaScript behavior in CodePen with humongous arrays - Stack Overflow

programmeradmin1浏览0评论

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 -

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.
  • Length of the array is not 114 Mil, (Chrome 2-3M, Firefox Dev 5 Mil, MS Edge 788k).

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.
  • Length of the array is not 114 Mil, (Chrome 2-3M, Firefox Dev 5 Mil, MS Edge 788k).
Share Improve this question edited Apr 5, 2016 at 15:38 TylerH 21.1k77 gold badges79 silver badges112 bronze badges asked Apr 5, 2016 at 11:53 ShrekOverflowShrekOverflow 6,9163 gold badges38 silver badges48 bronze badges 4
  • A small note: avg not changing is due to the precision of floating point numbers. At some point, the value of avg gets so large that adding a small number to it has no effect, because of the bit size of the mantissa. – Anders Marzi Tornblad Commented Apr 5, 2016 at 12:15
  • 1 may be issue with i<114000000 ? codepen without any arrays – vp_arth Commented Apr 5, 2016 at 12:17
  • @AndersTornblad Math.random() * 1000000 / 1000 should be in half cases more than 500, which is definitely not enough to be swallowed by rounding errors/precision for such a small number as 1261167461.290721 (for my current run) – zerkms Commented Apr 5, 2016 at 12:20
  • Ah, that is true... If the array stops growing (.push() fails), there will be no arr[i] to fetch the .x property from, which is more likely the reason for avg not growing. If all calls to .push() had succeeded, avg would still be off because of float errors. But I still don't know why avg is unchanged after the second loop. – Anders Marzi Tornblad Commented Apr 5, 2016 at 12:25
Add a comment  | 

2 Answers 2

Reset to default 33

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.

It's just codepen script runner restrictions.

I run script in Chrome Developer Tools and in Node.JS REPL - all seems ok.


Codepen docs

发布评论

评论列表(0)

  1. 暂无评论