iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0
Modern Web

小白大戰基礎網頁開發系列 第 17

D17 - Timers 開始倒數計時 (下)

  • 分享至 

  • xImage
  •  

昨天看完對 Timer (定時器) 的基本認識後, 今天來討論一下 setTimeout/setInterval 其他你還不知道的小事吧!

將附加參數傳遞給 setTimeout/setInterval

Example:

    function delayedMultiply() {
      // 當定時器關閉時,6 和 7 這兩個參數會被傳遞給 multiply 函式
      setTimeout(multiply, 3000, 5, 6);
    }
    function multiply(a, b) {
      console.log(a * b);
    }
  • 延遲 (delay) 後的任何參數最終都會傳遞給定時器函數 (timer function)
    • 不過在 IE 瀏覽器中是不起作用的, 必須另外創建一個中間(匿名)函數 → anonymous function 來傳遞參數

常見的 Timer 錯誤寫法

    // 在傳遞函數的時候誤多寫了()
    
    setTimeout(sayHi(), 3000); //wrong
    setTimeout(sayHi, 3000); // correct
    
    setTimeout(delayedMultiply(num1, num2), 3000); //wrong
    setTimeout(delayedMultiply, 3000, num1, num2); //correct

那麼好奇寶寶就會問說: 如果你有寫( ), 它實際上會做什麼?
答案是, 該函數會馬上被呼叫,而不是等待 3000 毫秒才被執行!

善用 timerId

我們有時需要在函數之間管理計時器, 追踪計時器,以便我們可以使用 clearInterval/clearTimeout 或是知道我們的頁面上計時器是否已經運行。
當我們無法將它們作為 local variable 進行追踪時,最好將它們存為 module-global variable(在 module 模式的範圍內,但程式中的所有函數都可以訪問)。

計時器不像你想像的那樣精準

關於之前提到的 JavaScript 單線程執行以及 event loop, 各個程式任務需要排入 event loop 內的 queue 來等待被執行。不過 timer 的優先權不是最高的,即使 timer 時間已經倒數完了但其他程式還沒執行完的話,也需要等待執行完後, timer 才會被執行。
因此 timer 不是想像中精確的,有時候可能會比 timer 本身設定的 delay 時間還晚才被執行。

    setTimeout(function example() {
     console.log ('delay 0 sec')
    }, 
    console.log ('Hi there!')

Result:

    Hi there!
    delay 0 sec

雖然 setTimeout 是 delay 0 秒鐘,但是主程序的 call stack 還有 console.log ('Hi there!') 還沒執行完, 因此 queue 中的 example( ) 會等待到 call stack 被清空後才會被執行。可以從這個之前推薦過的網站來觀察整個流程圖:

Loupe by Philip Roberts

製作一個從 10 倒數的定時器

有 coding 基礎的你回想一下,底下這個函數會立即且按順序打印每一行。如果我們想每 1 秒(1000ms)打印出每一行,那我們應該使用什麼樣的定時器 (timer) 呢?

    function startCountDown() {
      let count = 10;
      for (let i = count; i > 0; i--) {
        console.log(i + "...");
      }
      console.log("0!");
    }
    startCountDown()

初次嘗試:

    function startCountDown() {
      let i = 10;
      setInterval(function() {
        if (i === 0) {
          console.log("0!");
        } else {
          console.log(i + "...");
          i--;
        }
      }, 1000);
    }

solution 很接近正解, 不過還有個小問題: 當倒數到 0 時,我們的計時器不會停止 !

再次嘗試: 使用 setInterval , clearInterval 和設定 timerId 變數

    function startCountDown() {
      let i = 10;
      let timerId = setInterval(function() {
        if (i === 0) {
          clearInterval(timerId);
          console.log("0!");
        } else {
          console.log(i + "...");
          i--;
        }
      }, 1000);
    }

This works!
當呼叫 startCountDown 時,我們為計時器分配一個新的時間間隔 (interval),並在 10 開始倒數計時 1 秒, 1 秒下去… 當我們達到 0 時,我們需要從 window 的任務中把 interval 清除掉。


上一篇
D16 - Timers 開始倒數計時 (上)
下一篇
D18 - 淺談 Promise
系列文
小白大戰基礎網頁開發30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言