# Scheduling: setTimeout and setInterval

## setTimeout

```javascript
let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)

// 1 秒後執行函式
function sayHi() {
  alert('Hello');
}
setTimeout(sayHi, 1000);

// 帶入參數到函式
function sayHi(phrase, who) {
  alert( phrase + ', ' + who );
}
setTimeout(sayHi, 1000, "Hello", "John"); // Hello, John

// 如果第 1 個參數是字串會視為函式，但不推薦
setTimeout("alert('Hello')", 1000);

// 這樣的寫法比較好
setTimeout(() => alert('Hello'), 1000);

// setTimeout 要帶入函式而非結果
// wrong!
setTimeout(sayHi(), 1000);
```

### Canceling with clearTimeout

```javascript
// 取消 setTimeout 的設定
let timerId = setTimeout(() => alert("never happens"), 1000);
alert(timerId); // timer identifier

clearTimeout(timerId);
alert(timerId); // same identifier (doesn't become null after canceling)
```

## setInterval

```javascript
// 每隔一段一時間就會執行一次
let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...)

// repeat with the interval of 2 seconds
let timerId = setInterval(() => alert('tick'), 2000);

// after 5 seconds stop
setTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000);

// 當出現 alert/confirm/prompt，秒數仍在計算
```

## Recursive setTimeout

```javascript
// 規律性執行程式碼有 2 種選擇，一種用 setInterval，另一種用 recursive setTimeout。
/** instead of:
let timerId = setInterval(() => alert('tick'), 2000);
*/

let timerId = setTimeout(function tick() {
  alert('tick');
  timerId = setTimeout(tick, 2000); // (*)
}, 2000);

// 使用 recursive setTimeout 比較靈活，可以根據情況設定下一次執行時間。
let delay = 5000;

let timerId = setTimeout(function request() {
  ...send request...

  if (request failed due to server overload) {
    // increase the interval to the next run
    delay *= 2;
  }

  timerId = setTimeout(request, delay);

}, delay);

// 透過 recursive setTimeout 可以精準執行，setInterval 無法

let i = 1;
setInterval(function() {
  func(i);
}, 100);


```

透過 recursive setTimeout 每次延遲時間可以精準執行，setInterval 無法。

```javascript
// setInterval
let i = 1;
setInterval(function() {
  func(i);
}, 100);

// 如果函式執行時間超過延遲時間，JavaScript 引擎會等待函式執行完然後立刻執行下一個函式。
// 因為執行函式的時間被計算在延遲時間內，造成延遲時間不能精確執行。
```

![](/files/-LhhqOl4pFMAUU-GD1Vm)

```javascript
// setTimeout
let i = 1;
setTimeout(function run() {
  func(i);
  setTimeout(run, 100);
}, 100);

// setTimeout 會等函式執行完才開始計算延遲時間，所以可以準確執行。
```

![](/files/-LhhqYbVWhnInMjdjvP0)

### Garbage collection

```javascript
// 當 setTimeout/setInterval 沒有被清除，會一直存在記憶體內不會消失，如果函式指向外部變數
// 外部變數也不會消失，這會造成效能問題
// the function stays in memory until the scheduler calls it
setTimeout(function() {...}, 100);
```

## setTimeout(…,0)

```javascript
// setTimeout(func) 等目前程式碼執行完畢才會執行，換言之是異步函式
setTimeout(() => alert("World"));
alert("Hello");
// Hello
// World
```

### Splitting CPU-hungry tasks

```javascript
// 如果有一個很吃資源的執行腳本，會造成整個網頁當掉，這時可以分開程式碼，像是前 100 行先執行
// 後 100 行用 setTimeout 執行
// 下面程式碼執行時網頁當掉
let i = 0;
let start = Date.now();
function count() {
  // do a heavy job
  for (let j = 0; j < 1e9; j++) {
    i++;
  }
  alert("Done in " + (Date.now() - start) + 'ms');
}
count();

// 改成這樣執行時網頁仍可運作
// 第一次運算 i=1...1,000,000，第二次運算 i=1,000,001..2,000,000，直到 i=1,000,000,000
let i = 0;
let start = Date.now();
function count() {

  // do a piece of the heavy job (*)
  do {
    i++;
  } while (i % 1e6 != 0);
  
  if (i == 1e9) {
    alert("Done in " + (Date.now() - start) + 'ms');
  } else {
    setTimeout(count); // schedule the new call (**)
  }
}
count();

// 在開始執行前就先預設新的呼叫函式，這樣耗費時間較少。
let i = 0;

let start = Date.now();

function count() {

  // move the scheduling at the beginning
  if (i < 1e9 - 1e6) {
    setTimeout(count); // schedule the new call
  }

  do {
    i++;
  } while (i % 1e6 != 0);

  if (i == 1e9) {
    alert("Done in " + (Date.now() - start) + 'ms');
  }

}

count();
```

#### Minimal delay of nested timers in-browser

```javascript
// 在網頁端 HTML5 規定，經過 5 次巢狀 setTimeout 函式延遲時間至少為 4 ms，
// 但在 server 端沒有這個限制
let start = Date.now();
let times = [];

setTimeout(function run() {
  times.push(Date.now() - start); // 保存上次调用的延时

  if (start + 100 < Date.now()) alert(times); // 100 毫秒之后，显示延时信息
  else setTimeout(run, 0); // 没超过 100 毫秒则再进行调度
}, 0);

// 2,3,5,8,13*,19,24,29,35,40,44,49,55,60,65,71,76,81,87,97,101
```

### Allowing the browser to render

```javascript
// 執行一個很大的腳本，當更改網頁內容時，會等到函式執行完才顯示解果，用 setTimeout 可以觀察到
// 內容持續改變，可以用來展示進度。
// normal
<div id="progress"></div>

<script>
  let i = 0;

  function count() {
    for (let j = 0; j < 1e6; j++) {
      i++;
      // 将当前 i 值放到 <div> 内
      // （innerHTML 在以后具体章节会讲到，这行代码看懂应该没问题）
      progress.innerHTML = i;
    }
  }

  count();
</script>

// setTimeout
<div id="progress"></div>

<script>
  let i = 0;

  function count() {

    // do a piece of the heavy job (*)
    do {
      i++;
      progress.innerHTML = i;
    } while (i % 1e3 != 0);

    if (i < 1e9) {
      setTimeout(count);
    }

  }

  count();
</script>
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mistborn.gitbook.io/til-coding/javascript/scheduling-settimeout-and-setinterval.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
