昨天看完對 Timer (定時器) 的基本認識後, 今天來討論一下 setTimeout/setInterval 其他你還不知道的小事吧!
Example:
    function delayedMultiply() {
      // 當定時器關閉時,6 和 7 這兩個參數會被傳遞給 multiply 函式
      setTimeout(multiply, 3000, 5, 6);
    }
    function multiply(a, b) {
      console.log(a * b);
    }
    // 在傳遞函數的時候誤多寫了()
    
    setTimeout(sayHi(), 3000); //wrong
    setTimeout(sayHi, 3000); // correct
    
    setTimeout(delayedMultiply(num1, num2), 3000); //wrong
    setTimeout(delayedMultiply, 3000, num1, num2); //correct
那麼好奇寶寶就會問說: 如果你有寫( ), 它實際上會做什麼?
答案是, 該函數會馬上被呼叫,而不是等待 3000 毫秒才被執行!
我們有時需要在函數之間管理計時器, 追踪計時器,以便我們可以使用 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 被清空後才會被執行。可以從這個之前推薦過的網站來觀察整個流程圖:
有 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 清除掉。