如何設定 javascript 的 setInterval 停止時機

更新日期: 2024 年 11 月 14 日

在 JavaScript 中,setInterval 是一個常用的非同步函數,用來在指定的時間間隔中執行一段程式碼。

儘管它功能強大,但開發者在使用 setInterval 時經常會遇到一些常見的錯誤,導致程式無法按預期運行。

本文將介紹一個常見的錯誤範例,並解釋其原因和正確的寫法。

常見錯誤範例

以下是一段常見的倒數計時程式碼,但它無法正常運作:

let totalSec = 5;

function displayTime() {
  const countDown = setInterval(() => {
    console.log(totalSec);
    totalSec = totalSec - 1;
  }, 1000);

  if (totalSec < 0) {
    console.log('結束!');
    clearInterval(countDown);
  }
}

displayTime();

錯誤原因解析

在這段程式碼中,displayTime 函數內的 if (totalSec < 0) 判斷條件寫在 setInterval 外部。

因此,if 條件只會在 setInterval 設定的第一刻檢查一次(當 displayTime 函數第一次執行時),而後每隔 1 秒執行的 setInterval 回呼函數並不會再次檢查這個 if 條件。

這導致倒數計時會一直進行,並且無法在 totalSec 小於 0 時自動停止。

解決方法:將 if 條件放到 setInterval 內部

為了確保每次計時都檢查 totalSec 的值,我們應將 if (totalSec < 0) 條件放到 setInterval 的回呼函數內部。

這樣,每次回呼函數執行時都會檢查 totalSec 是否小於 0,並在條件滿足時清除計時器。

正確範例

以下是修正後的程式碼:

let totalSec = 5;

function displayTime() {
  const countDown = setInterval(() => {
    console.log(totalSec);
    totalSec = totalSec - 1;

    if (totalSec < 0) {
      console.log('結束!');
      clearInterval(countDown);
    }
  }, 1000);
}

displayTime();

修正要點

  • if 條件移入 setInterval 回呼函數內:這樣每次 setInterval 執行時,都會檢查 totalSec 是否小於 0。

    當條件成立時,執行 clearInterval(countDown) 來停止計時器。
  • 避免在 setInterval 外部檢查條件:因為外部的條件只會在 setInterval 初始化時執行一次,而無法在每次計時後進行檢查。

深入理解:為什麼 clearInterval 可以拿到 countDown 變數?

有些開發者可能會疑惑,clearInterval(countDown) 如何能夠正常運作?

畢竟 countDown 是在 setInterval 回呼函數內部呼叫的,而 countDown 變數的值是 setInterval 開始執行後才返回的。

JavaScript 的非同步與閉包機制

setInterval 是一個非同步函數,它在被呼叫時,會立即返回一個計時器 ID(通常是一個數字),並將這個 ID 指派給 countDown 變數。

我們可以使用這個 ID 來識別並控制這個計時器。以下是 setInterval 的執行流程:

  1. 立即返回計時器 ID:當 setInterval 被呼叫時,它會立即執行並返回計時器 ID,並將這個 ID 賦值給 countDown
  2. 設定回呼函數的週期執行setInterval 隨後會安排回呼函數每隔指定時間執行一次,但此時回呼函數尚未執行。
  3. 回呼函數每次到達間隔時排入事件佇列:回呼函數每到達間隔時才會被放入事件佇列,等待執行。

因為 countDownsetInterval 呼叫時就已經被賦值為計時器 ID,即使 clearInterval(countDown) 包含在回呼函數中,也可以正確地使用這個 ID 來停止計時器。

更深入的解釋:非同步並不意味著「排隊」

非同步並不代表 setInterval 函數本身會「去旁邊排隊」。

實際上,非同步的部分是 setInterval 所設定的回呼函數,而不是 setInterval 函數本身。

setInterval 函數執行後會立即返回計時器 ID,並不會等待回呼函數執行完畢才返回。

具體流程如下:

  1. setInterval 立即返回一個計時器 ID,這個 ID 是立即可用的。
  2. setInterval 開始設定間隔時間,並在每次到達間隔時將回呼函數放入事件佇列中等待執行。

因此,我們能夠在回呼函數中使用 clearInterval(countDown),因為 countDown 的值在回呼函數執行前已經被設置好了。

結論

在 JavaScript 中使用 setInterval 時,需要注意以下要點:

  1. 將條件檢查放在 setInterval 的回呼函數內部:這樣可以保證每次執行時都會檢查條件,確保計時器能在需要時停止。
  2. 理解 setInterval 的非同步特性setInterval 本身不會去排隊,而是回呼函數在每個間隔後才會被放入事件佇列,等待執行。
  3. 善用閉包與作用域:計時器 ID 是立即返回的,因此可以在回呼函數內正常使用來控制計時器。

正確掌握 setInterval 的使用方法,可以避免程式進入無窮迴圈,並提升程式碼的穩定性與可讀性。

希望本文能幫助您更深入地理解 setInterval 的運作邏輯與使用技巧。

Similar Posts