欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS面試必備之如何實(shí)現(xiàn)一個(gè)精確的倒計(jì)時(shí)

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

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

首先我們?cè)趯懙褂?jì)時(shí)的時(shí)候必須要考慮到三點(diǎn):準(zhǔn)確性、穩(wěn)定性、性能。接下來我們來一步一步實(shí)現(xiàn)一個(gè)準(zhǔn)確的定時(shí)器。

setInterval

我們先來簡(jiǎn)單實(shí)現(xiàn)一個(gè)倒計(jì)時(shí)的函數(shù):

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

example1(10);

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

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

簡(jiǎn)單來說意思就是,js因?yàn)槭菃尉€程的原因,如果前面有阻塞線程的任務(wù),那么就可能會(huì)導(dǎo)致setInterval函數(shù)延遲,這樣倒計(jì)時(shí)就肯定會(huì)不準(zhǔn)確,建議使用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中也說了,有很多因素會(huì)導(dǎo)致 setTimeout 的回調(diào)函數(shù)執(zhí)行比設(shè)定的預(yù)期值更久,比如嵌套超時(shí)、非活動(dòng)標(biāo)簽超時(shí)、追蹤型腳本的節(jié)流、超時(shí)延遲等等,詳情見developer.mozilla.org/zh-CN/docs/Web/API/setTimeout,總就就是和setInterval差不多,都可能會(huì)有延遲執(zhí)行的時(shí)候,這么一來如何提高倒計(jì)時(shí)的準(zhǔn)確性呢?

requestAnimationFrame

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

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

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

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

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

diffTime

要解決上述的問題,通過時(shí)間差值每次進(jìn)行對(duì)比就可以了。

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();
}

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

finally

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

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

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

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

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

根據(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();
    // 每次實(shí)際執(zhí)行的時(shí)間
    const executionTime = currentTime - startTimeRef.current;

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

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

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

    // 重置初始時(shí)間
    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)化思路,歡迎大家有更好的想法也可以隨時(shí)進(jìn)行探討~

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

相關(guān)文章

  • Javascript結(jié)合css實(shí)現(xiàn)網(wǎng)頁換膚功能

    Javascript結(jié)合css實(shí)現(xiàn)網(wǎng)頁換膚功能

    現(xiàn)在網(wǎng)站換皮膚是比較常見的功能,大多數(shù)論壇都有的,要想實(shí)現(xiàn)這樣效果可以看如下代碼.
    2009-11-11
  • JavaScript將數(shù)組轉(zhuǎn)換為鏈表的方法

    JavaScript將數(shù)組轉(zhuǎn)換為鏈表的方法

    這篇文章主要介紹了JavaScript將數(shù)組轉(zhuǎn)換為鏈表的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • javaScript 刪除確認(rèn)實(shí)現(xiàn)方法小結(jié)

    javaScript 刪除確認(rèn)實(shí)現(xiàn)方法小結(jié)

    因?yàn)閷?duì)于內(nèi)容的刪除是件很重要的事,所以一般的系統(tǒng)中,都需要?jiǎng)h除確認(rèn)一下,以免誤刪,具體的方法如下,大家可以參考下。
    2009-12-12
  • javascript感應(yīng)鼠標(biāo)圖片透明度顯示的方法

    javascript感應(yīng)鼠標(biāo)圖片透明度顯示的方法

    這篇文章主要介紹了javascript感應(yīng)鼠標(biāo)圖片透明度顯示的方法,涉及javascript針對(duì)鼠標(biāo)事件及圖片透明度操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • javascript實(shí)現(xiàn)省市區(qū)三級(jí)聯(lián)動(dòng)下拉框菜單

    javascript實(shí)現(xiàn)省市區(qū)三級(jí)聯(lián)動(dòng)下拉框菜單

    這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)省市區(qū)三級(jí)聯(lián)動(dòng)下拉框菜單很詳細(xì)的代碼,解決了大家實(shí)現(xiàn)javascript省市區(qū)三級(jí)聯(lián)動(dòng)下拉框菜單的問題,感興趣的小伙伴們可以參考一下
    2015-11-11
  • 詳解JavaScript中ora庫的使用教程

    詳解JavaScript中ora庫的使用教程

    使用 ora 這個(gè) JavaScript 庫可以在命令行應(yīng)用程序中提供漂亮的加載狀態(tài)提示,本文詳細(xì)介紹如何使用該庫,希望對(duì)大家有一定的幫助
    2024-02-02
  • 提升JS編程效率的19個(gè)實(shí)用技巧分享

    提升JS編程效率的19個(gè)實(shí)用技巧分享

    在實(shí)際工作中,開發(fā)者常面臨一些需巧妙編程解決的挑戰(zhàn),有時(shí)幾行代碼就能迎刃而解,本文整理了一系列實(shí)用代碼片段,助您輕松處理URL、DOM操作、事件處理等常見問題,希望對(duì)大家有所幫助
    2023-11-11
  • 使用JavaScript實(shí)現(xiàn)旋轉(zhuǎn)的彩圈特效

    使用JavaScript實(shí)現(xiàn)旋轉(zhuǎn)的彩圈特效

    這篇文章主要介紹了使用JavaScript實(shí)現(xiàn)旋轉(zhuǎn)的彩圈特效的相關(guān)資料,需要的朋友可以參考下
    2015-06-06
  • js獲取html文件的思路及示例

    js獲取html文件的思路及示例

    html文件如何獲取,有很多朋友對(duì)此表示疑問,在本文將為大家介紹下使用js的或許方法,感興趣的朋友可以參考下,希望對(duì)大家有所幫助
    2013-09-09
  • javascript中正則表達(dá)式語法詳解

    javascript中正則表達(dá)式語法詳解

    這篇文章主要介紹了javascript中正則表達(dá)式語法詳解,文章通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08

最新評(píng)論