js輪詢之setInterval踩坑小記

寫在開頭

最近負責的項目要接入計費功能,有了計費自然需要充值。說起充值,無外乎支付寶、微信和網銀了,然後前端調用充值接口後,需要起一個輪詢任務,去監聽支付狀態,根據輪詢結果來進行下一步的操作(當然了,有條件的大戶人家想要直接上websocket也沒問題)。所以,業務場景倒是很easy,梳理完邏輯我就吭哧吭哧開搞了…

發現問題

然而,當我基本寫完在調試時,發現每次輪詢停止了,頁面上的狀態會刷新兩次。當時的業務是需要我在支付狀態時顯示驗證碼過期的倒計時,然而倒計時結束後會重新計時一次,這裏的場景可以不關心,直觀的表現就是輪詢清除後狀態更新了兩次,而且是偶現!

import { useRef, useState } from 'react';

export const useRechargeResult = () => {
  const intervalRef = useRef<number | undefined>(void 0);
  const [rechargeStatus, setRechargeStatus] = useState(0);

  const getRechargeStatus = async (sequence: string) => {
    await axios.post('/bill/recharge/status', {
        sequence
      })
      .then(res => {
       setRechargeStatus(res?.data?.status || 0);
       
        // 1.充值中 2.充值成功 3.充值失敗 4.充值過期
        if (res?.data?.status !== 1) {
          clearPolling();
        }
      });
  };

  const startPolling = (sequence: string) => {
    intervalRef.current = setInterval(() => {
      getRechargeStatus(sequence);
    }, 1000);
  };

  const clearPolling = () => {
    clearInterval(intervalRef.current);
    intervalRef.current = void 0;
  };

  return {
    rechargeStatus,
    setRechargeStatus,
    startPolling,
    clearPolling
  };
};

bug原因

代碼如上,思考了良久,我覺得是js執行機制哪裏我疏忽了,還有爲什麼是偶現呢?於是乎,經過debuger後,我發現了問題所在。

假如getRechargeStatus 函數沒有then回調,clearInterval(timerId)可以很輕鬆的清除定時任務,不會讓它進入js執行棧中執行。

如果這樣寫const { data } = await getRechargeStatus() 或者await getRechargeStatus().then(res => {}),而且js現在處於setTimeout的回調函數已經執行並且在await狀態中,那麼js清除了setInterval的回調函數的執行(此時最後一次setTimeout的回調函數已經執行),但是卻沒有清除await getRechargeStatus()的回調函數,還在苦苦等待…。所以,最後一次輪詢的異步回調(即then中的代碼)會繼續執行,導致了輪詢停止後,頁面狀態又更新了一遍。

請求的時間越長,這個狀態(setTimeout的回調函數已經執行並且在await 狀態中)出現的概率越高。這就是爲什麼,偶現。

所以,我在回調中處理了一下,如果輪詢不存在的話,就不執行,問題解決了,手動狗頭。

  const getRechargeStatus = async (sequence: string) => {
    await httpBillClient
      .post('/bill/recharge/detail', {
        sequence
      })
      .then(res => {
        // 清除定時器時,並不能清除異步請求的回調,所以這裏要限制一下
        if (intervalRef.current) {
          setRechargeStatus(res?.data?.status || 0);
        }

        // 1.充值中 2.充值成功 3.充值失敗 4.充值過期
        if (res?.data?.status !== 1) {
          clearPolling();
        }
      });
  };

參考鏈接:https://blog.csdn.net/weixin_34309543/article/details/88838560

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章