All files / src/classes CoreLoopService.ts

93.33% Statements 28/30
85.71% Branches 12/14
100% Functions 7/7
93.33% Lines 28/30

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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132                                                                                                                                                  4x 4x 4x 4x       6x 3x 3x 3x 2x 2x         3x 3x 3x 3x             3x 3x       6x 6x 6x 6x   6x 4x 4x   2x 2x         8x 7x         4x          
import CoreService from './CoreService';
import type {
  ICoreCache,
  ICoreClient,
  ICoreKernel,
  ICoreKernelModule,
  ICorePresenter,
  IDataBase,
} from '../lib';
import { XUtil } from '../utils';
 
/**
 * CoreLoopService is an abstract service that runs a periodic loop task.
 *
 * @template K The kernel type that extends {@link ICoreKernel<any>}.
 * @template T The database type, defaults to {@link IDataBase<any, any>} or {@code null}.
 * @template P The client type, defaults to {@link ICoreClient} or {@code null}.
 * @template C The cache type, defaults to {@link ICoreCache} or {@code null}.
 * @template E The presenter type, defaults to {@link ICorePresenter<any>} or {@code null}.
 *
 * @extends CoreService<K, T, P, C, E>
 *
 * @param {string} name - Unique identifier for the service instance.
 * @param {number} timeout - Interval in milliseconds between loop executions.
 * @param {ICoreKernelModule<K, T, P, C, E>} module - The kernel module that supplies the service’s dependencies.
 * @param {boolean} [skipAutoStart] - If true, the service will not start automatically after construction.
 *
 * @method async start()
 *   Initiates the service loop. If the service is in the 'INIT' state, it schedules the first
 *   execution immediately; otherwise, it schedules subsequent executions based on the configured
 *   timeout. The method respects the {@link forceStop} flag to avoid restarting after a stop request.
 *
 * @method async startUp()
 *   Internal routine that transitions the service into a 'RUNNING' state and then
 *   executes the {@link loop} method. If a stop has been requested, it moves the service
 *   to a 'SLEEPING' state instead.
 *
 * @method async next()
 *   Moves the service into a 'SLEEPING' state and then restarts it, ensuring the next
 *   loop iteration begins after the configured timeout.
 *
 * @method async stop()
 *   Attempts to halt the service. It sets the {@link forceStop} flag and, depending on the
 *   current state, clears scheduled timers or waits briefly before retrying until the
 *   service is fully stopped.
 *
 * @method setRunning()
 *   Sets the internal state to 'RUNNING' unless a stop has been forced.
 *
 * @method setSleeping()
 *   Sets the internal state to 'SLEEPING'.
 *
 * @abstract async loop()
 *   Subclasses must implement this method to contain the logic that should execute
 *   on each loop cycle. The method is awaited by {@link startUp}.
 */
export default abstract class CoreLoopService<
  K extends ICoreKernel<any> = ICoreKernel<any>,
  T extends IDataBase<any, any> | null = any,
  P extends ICoreClient | null = any,
  C extends ICoreCache | null = any,
  E extends ICorePresenter<any> | null = any,
> extends CoreService<K, T, P, C, E> {
  interval: NodeJS.Timeout | null;
 
  timeOut: number;
 
  constructor(
    name: string,
    timeout: number,
    module: ICoreKernelModule<K, T, P, C, E>,
    skipAutoStart?: boolean,
  ) {
    super(name, module, skipAutoStart);
    this.interval = null;
    this.timeOut = timeout;
    this.startUp = this.startUp.bind(this);
  }
 
  async start(): Promise<any> {
    if (this.state === 'INIT') {
      this.setRunning();
      this.interval = setTimeout(this.startUp, 0);
    } else if (!this.forceStop) {
      this.verbose(this.state);
      this.interval = setTimeout(this.startUp, this.timeOut);
    }
  }
 
  async startUp(): Promise<any> {
    if (!this.forceStop) {
      this.setRunning();
      this.verbose(this.state);
      await this.loop();
    } else E{
      this.setSleeping();
    }
  }
 
  async next(): Promise<any> {
    this.setSleeping();
    await this.start();
  }
 
  async stop(): Promise<any> {
    this.debug('try to stop service');
    this.forceStop = true;
    this.debug('ForceStop');
    Iif (this.state === 'INIT') {
      this.debug('Service Exit 0');
    } else if (this.interval && this.state === 'SLEEPING') {
      clearInterval(this.interval);
      this.debug('Service Exit');
    } else {
      await XUtil.sleep(1000);
      await this.stop();
    }
  }
 
  setRunning() {
    if (!this.forceStop) {
      this.state = 'RUNNING';
    }
  }
 
  setSleeping() {
    this.state = 'SLEEPING';
  }
 
  abstract loop(): Promise<void>;
}