Promises.tick and (relatively) heavy computations

Hey, a (hopefully) quick question to clarify. The following example is taken from https://observablehq.com/@mbostock/animation-loops

{
  let i = 0;
  while (true) {
    yield Promises.tick(1000, ++i);
  }
}

Imagine if I was instead performing a heavy computation in the while loop, say one that takes 50ms on average, and I want to yield every 100 ms. If I try to implement that naively in the while loop, would the result be one tick every 150 ms or every 100 ms? In case of the former, what is the right way to ensure a constant tick rate?

I’m asking because in this little notebook on randomly answering multiple choice tests, the amount of samples I take every loop grows exponentially:

    const iterations = 10**(Math.log10(totalSamples)|0);
    for(let j = 0; j < iterations; j++){
      let correct = 0;
      // answer totalQuestions questions at random
      for (let i = 0; i < totalQuestions; i++){
        if (randVals[--remainingVals] < correctChoice) correct++;
        // generate new random values if we run out
        if (remainingVals === 0) {
          window.crypto.getRandomValues(randValsU8);
          remainingVals = randVals.length;
        }
      }
      rawData[correct]++;
      totalSamples++;
    }

The effect is nice, but for the last few loops I figure it might get a bit heavy, so I was curious what the best way to deal with that would be (I could use a worker but that is probably overkill).

1 Like

If you have something which always takes X ms inside your while loop and yield Promises.tick(Y, result) then if X < Y it should (after the first loop) tick every Y ms. If X > Y then the delay time will be rather more complicated: I think it’s X + Y - (X % Y) (?). If you can ensure that the other code in your loop will always run in less than 100 ms you should get a roughly constant tick rate.

Another approach is to save Date.now to a temporary variable lastYielded after each yield and then loop until Date.now is greater than lastYielded+100 before yielding again:

{
  const delay = 100;
  let i = 0;
  let lastYielded = Date.now();
  while (true) {
    while (Date.now() < lastYielded + delay) {
      // do stuff
      i++;
    }
    yield i;
    i = 0;
    lastYielded = Date.now();
  }
}
3 Likes

Thanks, that clarifies it :slight_smile: