All files / core/src/utils loop.ts

100% Statements 42/42
100% Branches 9/9
100% Functions 3/3
100% Lines 42/42

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 641x                           1x 8x 8x 8x 8x   8x 9x 1x 1x 1x 1x 1x 1x 1x 1x   8x   8x 8x 8x 29x   29x 29x   29x 5x 5x 29x 1x 1x 1x 8x 8x 9x   8x 8x 8x   8x 8x 8x 8x   8x 8x  
import { captureStack } from '../exception.js';
 
export type LoopFn = (fn: () => void) => Promise<number>;
export type StopFn = () => void;
export type MicroLooper = [LoopFn, StopFn];
 
/**
 * Creates a micro loop that executes a function repeatedly with a fixed time delay.
 * Each iteration is executed asynchronously and the loop continues until the maximum number of iterations is reached.
 *
 * @param timeout - The interval in milliseconds between each execution
 * @param steps - The maximum number of iterations to execute
 * @returns A tuple containing the loop function and stop function
 */
export function microloop(timeout: number, steps: number): MicroLooper {
  let isRunning = false;
  let currentStep = 0;
  let currentInterval = 0;
  let currentResolver: ((value: number) => void) | undefined = undefined;
 
  const loop = (fn: () => void) => {
    if (isRunning) {
      captureStack.warning.external(
        'Duplicated loop detected:',
        'Attempted to run a looper with a running looper.',
        'Multi loop not allowed',
        loop
      );
      return Promise.resolve(0);
    }
 
    isRunning = true;
 
    return new Promise<number>((resolve) => {
      currentResolver = resolve;
      currentInterval = setInterval(() => {
        currentStep++;
 
        try {
          fn();
 
          if (currentStep >= steps) {
            stop();
          }
        } catch (error) {
          captureStack.error.external('Walker execution failed.', error as Error);
          stop();
        }
      }, timeout) as never;
    });
  };
 
  const stop = () => {
    clearInterval(currentInterval);
    currentResolver?.(currentStep);
 
    isRunning = false;
    currentStep = 0;
    currentInterval = 0;
  };
 
  return [loop, stop];
}