# All files / src/utils delay.ts

100% Statements 23/23
100% Branches 4/4
85.71% Functions 6/7
100% Lines 22/22

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

    3x

    60x

    60x
    60x

    60x
    8x

    60x
    8x

    8x
    3x
    5x
    3x

    8x
    8x
    8x
    8x
    8x
    8x

    8x

    8x
    8x

    8x

     /**
    * Creates a function that batches calls and delays execution.
    * It ensures the 'targetFunction' is only called after a 'delayMs' wait period
    * AND after the previous execution has finished.
    *
    * Batch and Delay, collects multiple requests into a single group
    * and delays the execution of the target function.
    *
    * @param targetFunction the promise-returning function to delay,
    *        targetFunction should take in a single array
    * @param delayMs the number of milliseconds to delay
    * @returns delayedFunc that takes an optional argument (array, single value, or nothing)
    */
    export function delay<T>(targetFunction: (arg: T[]) => Promise<unknown>, delayMs: number) {
      let context: any;
      let itemsInQueue: T[] = [];

      let ongoingWorkPromise: Promise<unknown> = Promise.resolve();
      let currentScheduledBatch: Promise<unknown> | null = null;

      const sleep = (ms: number) => new Promise<void>((resolve) => {
        setTimeout(resolve, ms);
      });

      return function (this: any, newItem?: T | T[]): Promise<unknown> {
        context = this;

        // Add new item to queue when called
        if (Array.isArray(newItem)) {
          itemsInQueue = itemsInQueue.concat(newItem);
        } else if (newItem) {
          itemsInQueue.push(newItem);
        }

        // Schedule a new batch if there is none scheduled
        if (currentScheduledBatch === null) {
          const batchPromise = Promise.all([sleep(delayMs), ongoingWorkPromise])
            .then(async () => {
              currentScheduledBatch = null;
              const itemsToProcess = [...itemsInQueue];
              itemsInQueue = [];

              return targetFunction.apply(context, [itemsToProcess]);
            });

          currentScheduledBatch = batchPromise;
          ongoingWorkPromise = batchPromise.catch(() => {});
        }

        return currentScheduledBatch;
      };
    }