Продолжаем марафон универсальных утилит на JavaScript, которые я использую от проекта к проекту независимо от стека.

Base utility

~/utils/looperFactory.ts

type TLooperInstance = {
  start: (cb: () => void) => void;
  stop: () => void;
  restart: (cb: () => void) => void;
}

export const looperFactory = ({
  delay = 10 * 1000, // 10 seconds by default
  isFirstStartRequired = false,
}: {
  delay?: number;
  isFirstStartRequired?: boolean;
}): () => TLooperInstance => {
  let timer: NodeJS.Timeout;
  let wasStopped = false;
  let __counter = 0;

  return () => {
    const start = (cb: () => void) => {
      // console.log('Run');
      if (!wasStopped) {
        if (__counter === 0 && isFirstStartRequired) {
          cb();
        }
        timer = setTimeout(() => {
          // console.log('Looper done and will be restarted.');
          if (cb) { cb(); }
          start(cb);
        }, delay);
        __counter += 1;
      } // else console.log('Not started');
    };
    const stop = () => {
      // console.log('Looper stopped');
      wasStopped = true;
      clearTimeout(timer);
    };
    const restart = (cb: () => void) => {
      stop();
      wasStopped = false;
      __counter = 0;
      start(cb);
    };
    return {
      start, stop, restart
    };
  };
};

Usage in React hook (for example)

К примеру, есть требование отображать индикатор непрочитанных сообщений, но на проекте нет сокетов, зато есть возможность циклически делать запрос в соответствующее API.

import { useRef, useEffect } from 'react';
import { looperFactory } from '~/utils/looperFactory';

type TProps = {
  activePersonID: string | undefined;
  isAllowed: boolean;
}

export const useFreshNotificationsCount = ({
  activePersonID, isAllowed
}: TProps) => {
  const { fetchDmsNotificationsCount } = useApiService();
  const looperRef = useRef(looperFactory({
    delay: 20 * 1000,
    isFirstStartRequired: true,
  })());

  useEffect(() => {
    if (!isAllowed) {
      looperRef.current.stop();
      return;
    }
    looperRef.current.restart(() => {
      fetchDmsNotificationsCount({
        query: {
          activePersonID, // For example
        },
      })
        .then((_fetcherResponse) => {
          // NOTE: Use data for success case
        })
        .catch((_fetcherErr) => {
          // NOTE: Use erorr case
        });
    });
  }, [activePersonID, isAllowed]);
};