最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS面試必備之如何實現(xiàn)一個精確的倒計時

 更新時間:2024年03月10日 14:11:46   作者:大橘為重07  
又到了金三銀四的季節(jié)了,面試的各位同學要開始準備起來了,今天主要分享一個在面試中經(jīng)常被提到的一個面試題:倒計時,希望對大家有所幫助

又到了金三銀四的季節(jié)了,面試的各位同學要開始準備起來了,今天主要分享一個在面試中經(jīng)常被提到的一個面試題:倒計時。其實這個問題不僅是在面試中,也在我們的業(yè)務(wù)里也會經(jīng)常用到,所以到底如何才能寫好一個倒計時呢?

首先我們在寫倒計時的時候必須要考慮到三點:準確性、穩(wěn)定性、性能。接下來我們來一步一步實現(xiàn)一個準確的定時器。

setInterval

我們先來簡單實現(xiàn)一個倒計時的函數(shù):

function example1(leftTime){
	let t = leftTime;
  setInterval(() => {
    t = t - 1000;
    console.log(t);
  }, 1000);
}

example1(10);

可以看到使用setInterval即可,但是setInterval真的準確嗎?我們來看一下MDN中的說明:

如果你的代碼邏輯執(zhí)行時間可能比定時器時間間隔要長,建議你使用遞歸調(diào)用了setTimeout 的具名函數(shù)。例如,使用 setInterval() 以 5 秒的間隔輪詢服務(wù)器,可能因網(wǎng)絡(luò)延遲、服務(wù)器無響應(yīng)以及許多其他的問題而導(dǎo)致請求無法在分配的時間內(nèi)完成。

簡單來說意思就是,js因為是單線程的原因,如果前面有阻塞線程的任務(wù),那么就可能會導(dǎo)致setInterval函數(shù)延遲,這樣倒計時就肯定會不準確,建議使用setTimeout替換setInterval。

setTimeout

按照上述的建議將setInterval換為setTimeout后,我們來看下代碼:

function example2(leftTime) {
  let t = leftTime;
  setTimeout(() => {
    t = t - 1000;
    if (t > 0) {
			console.log(t);
      example2(t);
    }
    console.log(t);
  }, 1000);
}

MDN中也說了,有很多因素會導(dǎo)致 setTimeout 的回調(diào)函數(shù)執(zhí)行比設(shè)定的預(yù)期值更久,比如嵌套超時、非活動標簽超時、追蹤型腳本的節(jié)流、超時延遲等等,詳情見developer.mozilla.org/zh-CN/docs/Web/API/setTimeout,總就就是和setInterval差不多,都可能會有延遲執(zhí)行的時候,這么一來如何提高倒計時的準確性呢?

requestAnimationFrame

這里就不得不提一個新的方法requestAnimationFrame,它是一個瀏覽器 API,允許以 60 幀/秒 (FPS) 的速率請求回調(diào),而不會阻塞主線程。通過調(diào)用 requestAnimationFrame 方法瀏覽器會在下一次重繪之前執(zhí)行指定的函數(shù),這樣可以確保回調(diào)在每一幀之間都能夠得到適時的更新。

我們使用requestAnimationFrame結(jié)合setTimeout來優(yōu)化一下之前的代碼:

function example3(leftTime) {
  let t = leftTime;
  function start() {
    requestAnimationFrame(() => {
      t = t - 1000;
      setTimeout(() => {
        console.log(t);
        start();
      }, 1000);
    });
  }
  start();
}

這樣的話就可以保證我們倒計時的穩(wěn)定性,使用requestAnimationFrame還有一個好處就是,息屏或者切后臺的操作時,requestAnimationFrame是不會繼續(xù)調(diào)用函數(shù)的,但是如果只使用setTimeout或者setInterval的話,他們會在后臺一直執(zhí)行,顯而易見,requestAnimationFrame更加的節(jié)省性能開銷。

在切后臺或者息屏的實際執(zhí)行時會發(fā)現(xiàn),雖然性能上好了,但上述的方法在準確性上確出了問題,當回到頁面時,倒計時會接著切后臺時的時間執(zhí)行,這樣肯定是不對。

diffTime

要解決上述的問題,通過時間差值每次進行對比就可以了。

function example4(leftTime) {
  const now = new Date().getTime();
  function start() {
    requestAnimationFrame(() => {
      const diff = leftTime - (new Date().getTime() - now);
      setTimeout(() => {
        console.log(diff);
        start();
      }, 1000);
    });
  }
  start();
}

上面的代碼實現(xiàn)思路其實在實際的業(yè)務(wù)中已經(jīng)能夠滿足我們的使用場景,但是如果想要在面試的時候表現(xiàn)的再好一點其實還是有可以優(yōu)化空間的,那么還要怎么優(yōu)化呢?

finally

我們來仔細分析一下,如上圖所示,上面代碼每次在setTimeout時其實真正執(zhí)行的時間不可能完全是一秒,可能多也可能少,這樣時間一長之后執(zhí)行到結(jié)束時肯定會和實際時間有誤差,雖然在秒級的單位不一定能看出來,如果采用毫秒做單位的話肯定會有些許。那么應(yīng)該如何處理呢?

其實最終的實現(xiàn)思路有也很簡單,在每次在執(zhí)行setTimeout的時候,不要將每次setTimeout的時間都設(shè)置為1000ms,而是算出每次執(zhí)行實際setTimeout中執(zhí)行的函數(shù)話費的時間,根據(jù)實際執(zhí)行的時間算出下次setTimeout需要執(zhí)行的時間即可。至于下次執(zhí)行的時間應(yīng)該怎么算呢?我們來通過簡單的一個圖表來找出其中的規(guī)律,假設(shè)下圖是每次setTimeout執(zhí)行的時間分布圖所示:

Time時間executionTime實際執(zhí)行時間diffTime差值nextTime下次執(zhí)行時間
012002001000
1000900100900
200090001000
30008002001200
40001300100900

通過上面的圖表我們可以發(fā)現(xiàn) nextTime = executionTime > nextTime ? 1000 - diffTime : 1000 + diffTime。

還需要注意的時候,一般在實際業(yè)務(wù)時后端返回給我們剩余時間字段,通常都不會是整秒的,這樣我們第一次執(zhí)行setTimeout的時的執(zhí)行時間就需要處理一下,在倒計時最后結(jié)束時可以大大減少最終的時間誤差。

根據(jù)上述的思路我們來看一下最終封裝出來的react hooks:

const useCountDown = ({ leftTime, ms = 1000, onEnd }) => {
  const countdownTimer = useRef();
  const startTimer = useRef();
  const startTimeRef = useRef(new Date().getTime());
  const nextTimeRef = useRef(leftTime % ms);

  const [count, setCount] = useState(leftTime);

  const clearTimer = () => {
    countdownTimer.current && clearTimeout(countdownTimer.current);
    startTimer.current && clearTimeout(startTimer.current);
  };

  const startCountDown = () => {
    clearTimer();
    const currentTime = new Date().getTime();
    // 每次實際執(zhí)行的時間
    const executionTime = currentTime - startTimeRef.current;

    // 實際執(zhí)行時間大于上一次需要執(zhí)行的時間,說明執(zhí)行時間多了,差值為多出來的時間,否則為少了的時間
    const diffTime =
      executionTime > nextTimeRef.current
        ? executionTime - nextTimeRef.current
        : nextTimeRef.current - executionTime;

    // 剩余時間減去應(yīng)該執(zhí)行的時間
    setCount((count) => {
      const c = count - (count % ms === 0 ? ms : count % ms);
      if (c <= 0) return 0;
      return c;
    });

    // 算出下一次的時間 思路:本次的實際執(zhí)行時間>下一次執(zhí)行的時間 ?1000 - diffTime : 1000 + diffTime;
    nextTimeRef.current =
      executionTime > nextTimeRef.current ? ms - diffTime : ms + diffTime;

    // 重置初始時間
    startTimeRef.current = new Date().getTime();

    countdownTimer.current = setTimeout(() => {
      requestAnimationFrame(startCountDown);
    }, nextTimeRef.current);
  };

  useEffect(() => {
    setCount(leftTime);
    startTimer.current = setTimeout(
      startCountDown,
      nextTimeRef.current,
    );
    return () => {
      clearTimer();
    };
  }, [leftTime]);

  useEffect(() => {
    if (count <= 0) {
      clearTimer();
      onEnd && onEnd();
    }
  }, [count]);

  return count;
};

export default useCountDown;

除了上述的優(yōu)化思路,歡迎大家有更好的想法也可以隨時進行探討~

到此這篇關(guān)于JS面試必備之如何實現(xiàn)一個精確的倒計時的文章就介紹到這了,更多相關(guān)JS倒計時內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家

相關(guān)文章

  • TypeScript?SDK?Enum運行時404問題原因分析及解決方案

    TypeScript?SDK?Enum運行時404問題原因分析及解決方案

    TypeScript是一個強類型的JavaScript超集,通過添加靜態(tài)類型系統(tǒng)提升代碼可維護性和開發(fā)效率,這篇文章主要介紹了TypeScript?SDK?Enum運行時404問題原因分析及解決方案的相關(guān)資料,需要的朋友可以參考下
    2026-03-03
  • JavaScript實現(xiàn)網(wǎng)頁五子棋小游戲

    JavaScript實現(xiàn)網(wǎng)頁五子棋小游戲

    這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)網(wǎng)頁五子棋小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • JS事件循環(huán)機制event loop宏任務(wù)微任務(wù)原理解析

    JS事件循環(huán)機制event loop宏任務(wù)微任務(wù)原理解析

    這篇文章主要介紹了JS事件循環(huán)機制event loop宏任務(wù)微任務(wù)原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-08-08
  • 前端必須掌握的五種常用排序算法總結(jié)大全

    前端必須掌握的五種常用排序算法總結(jié)大全

    前端開發(fā)中掌握排序算法對于數(shù)據(jù)處理至關(guān)重要,本文介紹五種基本排序算法,冒泡排序、選擇排序、插入排序、快速排序和歸并排序,幫助開發(fā)者選擇合適的方法優(yōu)化性能,需要的朋友可以參考下
    2024-10-10
  • js網(wǎng)頁滾動條滾動事件實例分析

    js網(wǎng)頁滾動條滾動事件實例分析

    這篇文章主要介紹了js網(wǎng)頁滾動條滾動事件的用法,實例分析了javascript中window.onscroll監(jiān)控滾動條滾動事件的相關(guān)技巧,需要的朋友可以參考下
    2015-05-05
  • 前端實現(xiàn)Word在線預(yù)覽功能詳解

    前端實現(xiàn)Word在線預(yù)覽功能詳解

    這篇文章主要給大家介紹了關(guān)于前端實現(xiàn)Word在線預(yù)覽功能的相關(guān)資料,工作中經(jīng)常有時會遇到需要給用戶創(chuàng)建word文檔并實現(xiàn)word文檔在線預(yù)覽的需求,需要的朋友可以參考下
    2023-09-09
  • 純前端實現(xiàn)發(fā)布新版本主動檢測更新提醒方案

    純前端實現(xiàn)發(fā)布新版本主動檢測更新提醒方案

    快速迭代的軟件開發(fā)環(huán)境中,前端應(yīng)用的更新頻率日益加快,為了確保用戶能夠享受到最新的功能和修復(fù),開發(fā)者需要設(shè)計一套高效的更新提醒機制,這篇文章主要介紹了純前端實現(xiàn)發(fā)布新版本主動檢測更新提醒的相關(guān)資料,需要的朋友可以參考下
    2026-03-03
  • js指定日期增加指定月份的實現(xiàn)方法

    js指定日期增加指定月份的實現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于js指定日期增加指定月份的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2018-12-12
  • ES2020系列之空值合并運算符 ''??''

    ES2020系列之空值合并運算符 ''??''

    這篇文章主要介紹了ES2020系列之空值合并運算符 '??',文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • three.js實現(xiàn)3D地圖的項目實踐

    three.js實現(xiàn)3D地圖的項目實踐

    three.js 是一個在瀏覽器里把 3D 畫出來的圖形庫,本文就來詳細的介紹一下three.js實現(xiàn)3D地圖的項目實踐,感興趣的可以了解一下
    2026-03-03

最新評論

梧州市| 梅州市| 渭南市| 大田县| 平顶山市| 安平县| 如皋市| 静安区| 嘉兴市| 湖口县| 武城县| 乐至县| 腾冲县| 仁化县| 青川县| 连州市| 利津县| 镇江市| 马边| 永昌县| 余江县| 广平县| 宕昌县| 天门市| 思南县| 方城县| 肥城市| 武宁县| 科技| 永福县| 卓尼县| 南澳县| 武鸣县| 家居| 榆林市| 常山县| 昌宁县| 运城市| 小金县| 肃北| 桐城市|