TIL coding
  • Initial page
  • 排版
  • Flexbox
  • Grid
  • jQuery
  • Untitled
  • JavaScript
    • An Introduction to JavaScript
    • Hello, world!
    • Code structure
    • The modern mode, "use strict"
    • Variables
    • Data types
    • Type Conversions
    • Operators
    • Comparisons
    • Interaction: alert, prompt, confirm
    • Conditional operators: if, '?'
    • Logical operators
    • Loops: while and for
    • The "switch" statement
    • Functions
    • Function expressions and arrows
    • JavaScript specials
    • Comments
    • Ninja code
    • Automated testing with mocha
    • Polyfills
    • Objects
    • Garbage collection
    • Symbol type
    • Object methods, "this"
    • Object to primitive conversion
    • Constructor, operator "new"
    • Methods of primitives
    • Numbers
    • Strings
    • Arrays
    • Array methods
    • Iterables
    • Map, Set, WeakMap and WeakSet
    • Object.keys, values, entries
    • Destructuring assignment
    • Date and time
    • JSON methods, toJSON
    • Recursion and stack
    • Rest parameters and spread operator
    • Closure
    • The old "var"
    • Global object
    • Function object, NFE
    • The "new Function" syntax
    • Scheduling: setTimeout and setInterval
    • Decorators and forwarding, call/apply
    • Function binding
    • Currying and partials
    • Arrow functions revisited
    • Property flags and descriptors
    • Property getters and setters
    • Prototypal inheritance
    • F.prototype
    • Native prototypes
    • Prototype methods, objects without __proto__
    • The “class” syntax
    • Class inheritance
    • Static properties and methods
    • Private and protected properties and methods
    • Extending built-in classes
    • Class checking: "instanceof"
    • Mixins
    • Error handling, "try..catch"
    • Custom errors, extending Error
    • Introduction: callbacks
    • Promise
    • Promises chaining
    • Error handling with promises
    • Promise API
  • Bootstrap
    • Navbar
Powered by GitBook
On this page
  • setTimeout
  • Canceling with clearTimeout
  • setInterval
  • Recursive setTimeout
  • Garbage collection
  • setTimeout(…,0)
  • Splitting CPU-hungry tasks
  • Allowing the browser to render

Was this helpful?

  1. JavaScript

Scheduling: setTimeout and setInterval

setTimeout

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

// 取消 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

// 每隔一段一時間就會執行一次
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

// 規律性執行程式碼有 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 無法。

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

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

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

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

Garbage collection

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

setTimeout(…,0)

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

Splitting CPU-hungry tasks

// 如果有一個很吃資源的執行腳本,會造成整個網頁當掉,這時可以分開程式碼,像是前 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

// 在網頁端 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

// 執行一個很大的腳本,當更改網頁內容時,會等到函式執行完才顯示解果,用 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>
PreviousThe "new Function" syntaxNextDecorators and forwarding, call/apply

Last updated 5 years ago

Was this helpful?