On the vast number of timer, page self – salvation road

scenario

Multiple countdown functions exist on a page, or multiple timing synchronization functions exist on a project. General activities of the product thief Hook eight like this stuff

Vue rolled up

approach

  1. First define a function, which is implementedCountdown + Cancel Countdown + Add target time period + Delete target time period + update view function

Define a useCoreTimerMeter function, the use of the window. The UI redrawing execution requestAnimationFrame, setTimeout out.


interface AfferentTime {
  /** * Refresh frequency */
  rate: number;
  /** * view View */
  viewRefs: Ref<string[]>;
  /** * the end callback function */callback? :() = > void;
  /** * the end time, which is the key */ passed in
  endTime: Time;
}

interface TimeUkeyItem extends AfferentTime {
  /** * Last iteration time */beforeTime? : Dayjs;/** * callback asynchronous function */_resolve? :(value? : unknown) = > void;
}

type Time = Dayjs | string;

 /** * get a single time content */

const useCoreTimerMeter = (endTime? : Time) = > {
  // Check whether the automatic end function is enabledendTime && ! dayjs(endTime).isValid() && (endTime =undefined);
  // Count down the set
  const list: TimerItem[] = [];

  /** * The timer's return value */
  let cancelNumber: number;
  / / compatible
  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = setTimeout;
    window.cancelAnimationFrame = clearTimeout; }}Copy the code
  1. To start the central timerControlnamedstartTimerMeterControlEach loop must include actions such as deleting expiration times and updating views

  /** * timer Control */
  const startTimerMeterControl = () = > {
    if(! list.length)return;
    // Reset last time, new
    window.cancelAnimationFrame(cancelNumber);
    // Start looping logical content
    const currtTime = dayjs();
    // Update the view
    getUpdateList(currtTime);
    // Delete expired
    deleteItem(currtTime);
    // Determine whether the central timer is finished
     if (endTime && dayjs(currtTime).valueOf() >= dayjs(endTime).valueOf()) {
      cancelAccount();
      return;
    }
    cancelNumber = window.requestAnimationFrame(startTimerMeterControl);
  };

Copy the code
  1. Added time node functionality, wrapping promises, and inserting callback to make it easier to reach the target node and generate a callback. And sort the time List for each time to facilitate the following deletion logic (findIndex)
   /** * add countdown */
   const addMeter = (item: AfferentTime) = > {
    if (dayjs(uitemKey).isValid()&& dayjs(item.endTime).valueOf() > dayjs().valueOf()) {
      // Sort from time to time
      list.push(item);
      list.sort(
        (firstEl, secondEl) = >
          dayjs(firstEl.endTime).valueOf() - dayjs(secondEl.endTime).valueOf()
      );
      const itemCtn = item as TimeUkeyItem;
      if(! itemCtn.beforeTime) { itemCtn.beforeTime = dayjs(); }return new Promise((resolve) = > (itemCtn._resolve = resolve));
    }
    throw new Error("Please pass in the key, the object that matches the time.");
  };
Copy the code
  1. Update eligibleRef<string[]>The node view
  /** * get the list of values to update
  const getUpdateList = (currtTime) = > {
     list.forEach((item) = > {
      if (
        dayjs(currtTime).valueOf() - dayjs(item.beforeTime).valueOf() >=
        item.rate!
      ) {
        / / updateditem.beforeTime = currtTime; updateTheView(currtTime, item); }}); };/** * Update the view */
  const updateTheView = (currtTime: Time, item: TimeUkeyItem) = > {
    // select * from *;
    const list = timeGapNumber(dayjs(item.endTime).valueOf() - dayjs(currtTime).valueOf());
    / / update
    item.viewRefs.value = list;
  };
  
  /** * time split */
const timeGapNumber = (time_dis: number): string[] => {
  if (time_dis < 0) {
    // The deadline has passed
    return Array(5).fill("00");
  } else {
    // Calculate the difference of days
    const days = Math.floor(time_dis / (24 * 3600 * 1000));
    // Count the hours
    const leave1 = time_dis % (24 * 3600 * 1000); // Count the number of milliseconds left after the number of days
    const hours = Math.floor(leave1 / (3600 * 1000));
    // Calculate the difference in minutes
    const leave2 = leave1 % (3600 * 1000); // Count the number of milliseconds left after the number of hours
    const minutes = Math.floor(leave2 / (60 * 1000));
    // Count the difference in seconds
    const leave3 = leave2 % (60 * 1000); // Count the number of milliseconds left after the number of hours
    const second = Math.floor(leave3 / 1000);
    // Count milliseconds
    const millisecond = leave3 % 1000; // Count the number of milliseconds left after the number of hours
    return [
      fillLength(`${days}`),
      fillLength(`${hours}`),
      fillLength(`${minutes}`),
      fillLength(`${second}`),
      `${millisecond}`.slice(0.1)]; }};/** */
const fillLength = (item: string): string= > {
  if (item.length === 1) {
    return ` 0${item}`;
  } else {
    returnitem; }};Copy the code
  1. If an expired node is deleted, the deletion succeeds using the new logicfindIndexHigher order function, delete fixed point range. And performPromise.resolve, callback
  /** * Delete the expected time */
  const deleteItem = (currtTime? : Time) = > {
  / / critical
    if (list.length) {
      if (dayjs(list[list.length - 1].endTime).valueOf() <= dayjs(currtTime).valueOf()) {
        list[list.length - 1].callback? . (); list[list.length -1]._resolve? . (); list.splice(0, list.length);
        return; }}if (list.length === 1) {
      if (dayjs(list[0].endTime).valueOf() <= dayjs(currtTime).valueOf()) {
        list[0].callback? . (); list[0]._resolve? . (); list.splice(0.1);
      }
      return;
    }
    let endIndex = list.findIndex((item) = > {
      if (dayjs(item.endTime).valueOf() <= dayjs(currtTime).valueOf()) {
        // Call callback before deletingitem.callback? . (); item._resolve? . ();return false;
      } else {
        return true; }});if(endIndex ! =null) list.splice(0, endIndex + 1);
    return list;
  };
Copy the code
  1. Listening to thelistTo determine whether it is necessary to turn on the central timer (watch)
watch(list, list.length ? startTimerMeterControl : cancelAccount);
Copy the code

7: The useCoreTimerMeter only needs to throw out additions internally, and the control of internal start and delete is handed over to internal processing. Finally, the singleton is generated, which is globally unique

/** * Global singleton central timer */
const useSingleCoreTimerMeter = (() = > {
  let instance: CoreTimerMeterResult;
  return (endTime? : Time) = > {
    if(! instance) { instance = useCoreTimerMeter(endTime); }returninstance; }; }) ();export { useSingleCoreTimerMeter };
Copy the code