3

I was trying to implement a seeded pseudo-random generator. It will produce random numbers until the time is up. The code is below

function* pseudoRandom(seed) {
  let random = seed;
  let flag = true;
  setTimeout(() => {flag = !flag}, 100); // stop generating after 100ms
  while (flag) {
    random = random * 16807 % 2147483647;
    yield random;
  }
}

let generator = pseudoRandom(1);

console.log([...generator]);

But I still get an error saying FATAL ERROR: invalid array length Allocation failed - JavaScript heap out of memory. I guess the loop didn't stop. So what has gone wrong?

Joji
  • 4,703
  • 7
  • 41
  • 86

1 Answers1

4

All the synchronous code in a script will be executed before callbacks get a chance to run. If you have code that blocks (such as endlessly yielding in a while loop without stopping), the callback will never have a chance to run.

For similar reasons, the following code will never exit the while loop:

let flag = false;
setTimeout(() => flag = true);
while (!flag) {
  // do something
}

Check to see if 100ms have passed inside the loop instead:

function* pseudoRandom(seed) {
  let random = seed;
  const start = Date.now();
  while (Date.now() - start < 100) { // stop generating after 100ms
    random = random * 16807 % 2147483647;
    yield random;
  }
}

let generator = pseudoRandom(1);

console.log([...generator].length);

Do note that the number of elements generated in the 100 ms may have a very large range, depending on what else the processor/browser is doing in that time.

Thanks Kaiido, To avoid blocking the visible UI, you could consider moving the blocking part of the script to a worker:

// https://stackoverflow.com/questions/10343913/how-to-create-a-web-worker-from-a-string
// I put in a Stack Snippet for live demonstration
// in a real project, put this in a separate file
const workerFn = () => {
  function* pseudoRandom(seed) {
    let random = seed;
    const start = Date.now();
    while (Date.now() - start < 100) { // stop generating after 100ms
      random = random * 16807 % 2147483647;
      yield random;
    }
  }
  self.onmessage = ({ data }) => {
    let generator = pseudoRandom(1);
    const arr = [...generator];
    self.postMessage(arr.length);
  };
};
const workerFnStr = `(${workerFn})();`;
const blob = new Blob([workerFnStr], { type: 'text/javascript' });
const worker = new Worker(window.URL.createObjectURL(blob));

worker.onmessage = ({ data: length }) => console.log(length);
worker.postMessage(1234);
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Note that 100ms is quite a long time. That's 6 frames on a 60hz monitor. Depending on what happens in the screen, eg. if you have some animation / transition the user will probably notice it and think of it as a bug in your page. Better move such blocking scripts to a parallel thread. – Kaiido May 31 '19 at 02:17