太奇怪了檩互!為什么我的計時器出了這么大誤差...崩潰中

引言:計時器的困惑

PM:嘿竞滓,你來看一下這個倒計時組件匪燕,為什么時間總是跳得這么不準確轿亮?我們的用戶可是每一秒都在盯著看呢纬凤!

FE:嗯,這個問題我也注意到了笤昨。我猜想可能是因為JavaScript的定時器不夠精確所導(dǎo)致的祖驱。你知道的,JavaScript的定時器并不是真正的實時瞒窒。

PM:不是實時捺僻?那我們的促銷活動豈不是要泡湯了?用戶體驗可不能因為這個而受到影響案汀陵像!

FE:放心吧,我知道這很重要寇壳。我會深入調(diào)查具體原因醒颖,并找到一個解決方案的。我們的倒計時組件會準時如一壳炎,準確無誤泞歉!

PM:那就拜托了!我可不想看到用戶因為這個小bug而離我們而去匿辩。

所以腰耙,讓我們揭開這個“時間誤差”的謎團,并確保我們的倒計時組件能夠精準無誤地幫助用戶倒數(shù)每一秒铲球!

正文:倒計時組件的誤差之謎

當你在開發(fā)這個功能時挺庞,你可能會首先寫出像下面這樣的代碼:

import { FC, useState, useEffect } from "react";
import "./_style.scss";

/** @description 每秒剩余秒數(shù)減1 */
export const CountOne: FC<{
  startTime: number;
}> = (props) => {
  const { startTime } = props;

  const [restSec, setRestSec] = useState(startTime);

  useEffect(() => {
    // 創(chuàng)建一個定時器,每隔1秒執(zhí)行一次
    const inter = setInterval(() => {
      // 更新restSec狀態(tài)稼病,每秒減一
      setRestSec(oldVal => oldVal - 1);
    }, 1000);

    return () => {
      clearInterval(inter);
    };
  }, []);

  return (
    <div className="pub-style">
      <p className="pub-style-text">每秒減一方案:</p>
      <p className="pub-style-text">
        <span className="pub-style-text-span">倒計時秒數(shù):</span>
        <span className="pub-style-text-span">{restSec}</span>
      </p>
    </div>
  );
};

每秒剩余秒數(shù)減1选侨,這個邏輯看似并無問題。然而然走,經(jīng)過一段時間的運行援制,或者在電腦合蓋后再運行,你可能會發(fā)現(xiàn)實際值與期望值之間存在較大的偏差芍瑞。

出大差錯了

如圖所示晨仑,第一種方案即為執(zhí)行一段時間后上述代碼的執(zhí)行結(jié)果。是不是很離譜拆檬?

這到底是哪里出了問題呢洪己?于是,我把這段代碼拿去詢問ChatGPT秩仆,給出的回答如下:

  1. JavaScript定時器的不精確性

    • JavaScript中的setTimeoutsetInterval函數(shù)并不能保證完全精確的時間間隔码泛,因為它們受到了事件循環(huán)的影響。如果頁面或者瀏覽器忙于其他任務(wù)澄耍,定時器的回調(diào)可能會被推遲執(zhí)行噪珊。
  2. 瀏覽器的最小化或標簽頁切換

    • 當用戶最小化瀏覽器或切換到其他標簽頁時晌缘,瀏覽器可能會減慢或暫停定時器的回調(diào),以節(jié)省資源痢站。
  3. 頁面渲染的性能問題

    • 如果頁面存在復(fù)雜的DOM操作或者JavaScript執(zhí)行效率低下磷箕,可能會影響倒計時組件的更新頻率。

怎么辦:倒計時組件的誤差之謎與解決策略

當我們嘗試用JavaScript來實現(xiàn)倒計時功能時阵难,通常會使用setInterval函數(shù)岳枷。然而,setInterval函數(shù)并不能保證完全精確的時間間隔呜叫,因為它的執(zhí)行受到很多因素的影響空繁,比如頁面的渲染速度、瀏覽器的性能等朱庆。

第一種方案:使用Web Workers

Web Workers允許我們在后臺線程中運行代碼盛泡,避免了主線程的擁堵,可以提高定時器的準確性娱颊。但是傲诵,Web Workers無法訪問DOM,因此不適用于需要實時渲染的場景箱硕。

第二種方案:時間戳比對

我們可以通過存儲一個初始時間戳拴竹,并在每次回調(diào)中比對當前時間戳,以計算真實經(jīng)過的時間剧罩。這種方法比單純依賴setInterval的回調(diào)更為精確栓拜。修改后的代碼如下:

import { FC, useState, useEffect } from "react";
import { getUnixTime } from "date-fns";

import "./_style.scss";

/** @description 每秒都用deadline - now */
export const CountUseNow: FC<{
  deadlineTime: number;
}> = (props) => {
  const { deadlineTime } = props;

  const [restSec, setRestSec] = useState(
    deadlineTime - getUnixTime(Date.now())
  );

  useEffect(() => {
    // 每隔1秒(1000毫秒)執(zhí)行一次
    // setRestSec(deadlineTime - getUnixTime(Date.now()))
    // 操作,使得restSec的值每秒更新一次
    const inter = setInterval(() => {
      setRestSec(() => deadlineTime - getUnixTime(Date.now()));
    }, 1000);

    return () => {
      clearInterval(inter);
    };
  }, []);

  return (
    <div className="pub-style">
      <p className="pub-style-text">每秒都用deadline減now方案:</p>
      <p className="pub-style-text">
        <span className="pub-style-text-span">倒計時秒數(shù):</span>
        <span className="pub-style-text-span">{restSec}</span>
      </p>
    </div>
  );
};

以上代碼中惠昔,我們使用了getUnixTime函數(shù)來獲取當前時間的秒級時間戳菱属,然后將其與設(shè)定的截止時間進行比對,從而得到剩余時間舰罚。

第三種方案:使用requestAnimationFrame

對于需要高頻更新的倒計時,我們可以使用requestAnimationFrame薛耻。它能保證在每次瀏覽器重繪之前更新時間营罢,從而提高準確性。但是饼齿,在非動畫場景下饲漾,使用頻率較低。

第四種方案:優(yōu)化頁面性能

確保DOM操作和JavaScript執(zhí)行盡可能高效缕溉,以減少對倒計時組件更新的干擾考传。這需要針對具體的應(yīng)用場景進行優(yōu)化,無法作為通用的解決方案证鸥。

經(jīng)過比較僚楞,第二種方案修改成本較低且滿足預(yù)期勤晚,更適合在大多數(shù)情況下使用。當然泉褐,具體選擇哪種方案還需要根據(jù)實際的應(yīng)用場景來決定赐写。

結(jié)論:追求精準的倒計時

我們探討了前端開發(fā)中倒計時組件出現(xiàn)誤差的原因,并提供了幾種解決方法膜赃。通過這些技術(shù)手段挺邀,我們可以極大地提高倒計時的準確性,確保用戶體驗不受影響√現(xiàn)在端铛,我們可以告別那些“崩潰中”的時刻,迎接一個更加精準和可靠的倒計時體驗疲眷。

致謝

ChatGPT 與 文心一言對本文亦有貢獻

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末禾蚕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子咪橙,更是在濱河造成了極大的恐慌夕膀,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件美侦,死亡現(xiàn)場離奇詭異产舞,居然都是意外死亡,警方通過查閱死者的電腦和手機菠剩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門易猫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人具壮,你說我怎么就攤上這事准颓。” “怎么了棺妓?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵攘已,是天一觀的道長。 經(jīng)常有香客問我怜跑,道長样勃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任性芬,我火速辦了婚禮峡眶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘植锉。我一直安慰自己辫樱,他們只是感情好,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布俊庇。 她就那樣靜靜地躺著狮暑,像睡著了一般鸡挠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上心例,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天宵凌,我揣著相機與錄音,去河邊找鬼止后。 笑死瞎惫,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的译株。 我是一名探鬼主播瓜喇,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼歉糜!你這毒婦竟也來了乘寒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤匪补,失蹤者是張志新(化名)和其女友劉穎伞辛,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夯缺,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡蚤氏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了踊兜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竿滨。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捏境,靈堂內(nèi)的尸體忽然破棺而出于游,到底是詐尸還是另有隱情,我是刑警寧澤垫言,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布贰剥,位于F島的核電站,受9級特大地震影響筷频,放射性物質(zhì)發(fā)生泄漏鸠澈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一截驮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧际度,春花似錦葵袭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蓬网。三九已至,卻和暖如春鹉勒,著一層夾襖步出監(jiān)牢的瞬間帆锋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工禽额, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锯厢,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓脯倒,卻偏偏與公主長得像实辑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子藻丢,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容