1枚のDEEP DIVEサマリー(タイマー)


タイマ


コールスケジューリング


関数を明示的に呼び出すと、関数はすぐに実行されます.
function add(a, b) {
  return a + b;
}

console.log(add(2, 5)) >>> 7;
関数を明示的に呼び出さず、所定の関数呼び出しが一定時間後に呼び出される場合は、タイマ関数が使用されます.これをコールスケジューリング(scheduling a call)と呼ぶ.
javascript
①タイマーを生成できるタイマー関数settimeout/setInterval
②タイマーのタイマー関数を削除できるclearTimeout/clearInterval
提供する
タイマ関数はECMAScript仕様で定義されたビルダーではありません.ブラウザ環境とノード.js環境では、タイマ関数をグローバルオブジェクトとして使用する方法が提供されています.すなわち、タイマ関数はホストオブジェクトである.
settimeout関数で生成されたタイマは1回のみ実行され、setInterval関数で生成されたタイマは繰り返し操作されます.
JavaScriptエンジンには実行コンテキストスタックが1つしかないため、2つ以上のタスクを同時に実行することはできません.つまり、JavaScriptエンジンは単一スレッドで動作します.したがって、タイマ関数は非同期処理で動作する.

タイマ関数


setTimeout / clearTimeout

const timeoutdId = setTimeout(func|code[, delay, param1, param2, ...]);
パラメータはfuncタイマが期限切れになった後、呼び出したコールバック関数のコールバック関数ではなく、コードを文字列として渡すことができることを示します.この場合、コード文字列はタイマが期限切れになった後に解釈および実行されます.遅延タイマの有効期限(ミリ秒単位)は、settimeout関数によって、遅延時間のみで動作するタイマを生成します.パラメータ伝達が省略されている場合は、デフォルト値0を指定します.param1, param2, ...呼び出しスケジューリングコールバック関数に渡す必要があるパラメータがある場合は、3番目の後続パラメータに渡すことができます.
// 1초(1000ms) 후 타이머가 만료되면 콜백 함수가 호출된다.
setTimeout(() => console.log("Hi!"), 1000);

// 세 번째 인수로 문자열 'Lee' 전달
setTimeout((name) => console.log(`Hi! ${name}.`), 1000, "Lee");

// 두 번째 인수(delay)를 생략하면 기본값 0이 지정된다.
setTimeout(() => console.log("Hello!"));
settimeout関数は、生成されたタイマの一意の識別を識別できる一意の識別を返します.settimeout関数が返すタイマidは
①ブラウザ環境では数値です.
② Node.js環境でのオブジェクト
settimeout関数から返されるタイマidをcleartimeout関数のパラメータに渡すことで、タイマをキャンセルできます.
const timerId = setTimeout(() => console.log("Hi!"), 1000);
console.log(timeId);

clearTimeout(timerId);

ブラウザで表示



setInterval/ clearInterval


setInterval関数は、2番目の引数が伝達される時間(ミリ秒、1/1000秒)で動作を繰り返すタイマを生成します.
setIntervalの最初のパラメータコールバック関数は、2番目のパラメータが伝達された時間内に呼び出しを繰り返すようにスケジューリングされる.
const timeoutdId = setInterval(func|code[, delay, param1, param2, ...]);
setInterval関数は、生成されたタイマを識別できる一意のタイマidを返す.Node.js環境の場合、オブジェクトが返されます.
let count = 1;

const timeoutId = setInterval(() => {
  console.log(count); // 1 2 3 4 5

  if (count++ === 5) clearInterval(timeoutId);
}, 1000);

console.log("timeoutId: ", timeoutId);

Node.js環境での表示



後退と制限


スクロール、resize、mousemoveなどのイベントが短時間間隔で連続して発生します.これらのイベントにバインドされたイベントハンドラが過度に呼び出され、パフォーマンスに問題が発生する可能性があります.ジッタおよび制限は、短時間間隔で連続的に発生するイベントをグループ化することによって、イベントハンドラの過度な呼び出しを防止するプログラミング技術である.
<!DOCTYPE html>
<html>
  <body>
    <button>click me</button>
    <pre>일반 클릭 이벤트 카운터    <span class="normal-msg">0</span></pre>
    <pre>디바운스 클릭 이벤트 카운터 <span class="debounce-msg">0</span></pre>
    <pre>스로틀 클릭 이벤트 카운터   <span class="throttle-msg">0</span></pre>
    <script>
      const $button = document.querySelector("button");
      const $normalMsg = document.querySelector(".normal-msg");
      const $debounceMsg = document.querySelector(".debounce-msg");
      const $throttleMsg = document.querySelector(".throttle-msg");

      const debounce = (callback, delay) => {
        let timerId;
        return (event) => {
          if (timerId) clearTimeout(timerId);
          timerId = setTimeout(callback, delay, event);
        };
      };

      const throttle = (callback, delay) => {
        let timerId;
        return (event) => {
          if (timerId) return;
          timerId = setTimeout(
            () => {
              callback(event);
              timerId = null;
            },
            delay,
            event
          );
        };
      };

      $button.addEventListener("click", () => {
        $normalMsg.textContent = +$normalMsg.textContent + 1;
      });

      $button.addEventListener(
        "click",
        debounce(() => {
          $debounceMsg.textContent = +$debounceMsg.textContent + 1;
        }, 500)
      );

      $button.addEventListener(
        "click",
        throttle(() => {
          $throttleMsg.textContent = +$throttleMsg.textContent + 1;
        }, 500)
      );
    </script>
  </body>
</html>

返品


イベントが短時間間隔で連続して発生すると、disbounceはイベントハンドラを呼び出さず、一定時間後にイベントハンドラを1回だけ呼び出す.
すなわち、コールバックは、イベントハンドラが最後に1回だけ呼び出されるように、短時間間隔で発生したイベントをグループ化することができる.

コード時計のYouTube講座
  • の最後のイベント(space)に基づいて、デフォルトの
  • である一定時間後に再呼び出されます.
  • の最後のイベントが発生してから(赤)、リカバリステータス(緑)で新しいイベントを再実行できます.
  • テキスト入力フィールドのinputイベントが短い時間間隔で連続して発生する場合、切り替えによって要求(例えばAjax)の頻度を低減し、サーバの負荷を軽減することができる.
    <!DOCTYPE html>
    <html>
      <body>
        <input type="text" />
        <div class="msg"></div>
        <script>
          const $input = document.querySelector("input");
          const $msg = document.querySelector(".msg");
    
          const debounce = (callback, delay) => {
            let timerId;
            // debounce 함수는 timerId를 기억하는 클로저를 반환한다.
            return (event) => {
              // delay가 경과하기 이전에 이벤트가 발생하면 이전 타이머를 취소하고
              // 새로운 타이머를 재설정한다.
              // 따라서 delay보다 짧은 간격으로 이벤트가 발생하면 callback은 호출되지 않는다.
              if (timerId) clearTimeout(timerId);
              timerId = setTimeout(callback, delay, event);
            };
          };
    
          // debounce 함수가 반환하는 클로저가 이벤트 핸들러로 등록된다.
          // 300ms보다 짧은 간격으로 input 이벤트가 발생하면 debounce 함수의 콜백 함수는
          // 호출되지 않다가 300ms 동안 input 이벤트가 더 이상 발생하면 한 번만 호출된다.
          $input.oninput = debounce((e) => {
            $msg.textContent = e.target.value;
          }, 300);
        </script>
      </body>
    </html>

    検索ウィンドウでクリックイベント(e.target.value)ごとにajaxリクエストを送信するよりも、デバッガによって最後のイベントにajaxリクエストを送信する方が、サーバ負荷を効果的に軽減できます.

    スロットルバルブ


    Throttleは、イベントが短時間間隔で連続的に発生しても、イベントハンドラが一定時間間隔で最大1回呼び出すことを可能にする.
    短い間隔で連続的に発生するイベントのグループ化を制限し、イベントハンドラを一定時間呼び出すための呼び出しサイクルを作成します.

    コード時計のYouTube講座
  • イベントが継続するも、上記の300 ms間隔で
  • がトリガーされる.
  • スローで指定された遅延(300ミリ秒)の間に発生するイベントは、
  • に無視されます.
    <!DOCTYPE html>
    <html>
      <head>
        <style>
          .container {
            width: 300px;
            height: 300px;
            background-color: rebeccapurple;
            overflow: scroll;
          }
    
          .content {
            width: 300px;
            height: 1000vh;
          }
        </style>
      </head>
      <body>
        <div class="container">
          <div class="content"></div>
        </div>
        <div>
          일반 이벤트 핸들러가 scroll 이벤트를 처리한 횟수:
          <span class="normal-count">0</span>
        </div>
        <div>
          스로틀 이벤트 핸들러가 scroll 이벤트를 처리한 횟수:
          <span class="throttle-count">0</span>
        </div>
    
        <script>
          const $container = document.querySelector(".container");
          const $normalCount = document.querySelector(".normal-count");
          const $throttleCount = document.querySelector(".throttle-count");
    
          const throttle = (callback, delay) => {
            let timerId;
            // throttle 함수는 timerId를 기억하는 클로저를 반환한다.
            return (event) => {
              // delay가 경과하기 이전에 이벤트가 발생하면 아무것도 하지 않다가
              // delay가 경과했을 때 이벤트가 발생하면 새로운 타이머를 재설정한다.
              // 따라서 delay 간격으로 callback이 호출된다.
              if (timerId) return;
              timerId = setTimeout(
                () => {
                  callback(event);
                  timerId = null;
                },
                delay,
                event
              );
            };
          };
    
          let normalCount = 0;
          $container.addEventListener("scroll", () => {
            $normalCount.textContent = ++normalCount;
          });
    
          let throttleCount = 0;
          // throttle 함수가 반환하는 클로저가 이벤트 핸들러로 등록된다.
          $container.addEventListener(
            "scroll",
            throttle(() => {
              $throttleCount.textContent = ++throttleCount;
            }, 1000)
          );
        </script>
      </body>
    </html>
  • スクロールイベントは、ユーザスクロール時に短時間間隔で連続する
  • が発生する.
  • throttle関数により呼び出しサイクルを作成し、短時間間隔でイベントを連続的に呼び出す過剰なイベントハンドラを防止する
  • 手書き体は、スクロールイベントの処理や無限スクロールUIの実現などに用いることができる

    Lodash


    Lodash公式文書
    例によってthrottやdebonseに関するコードを直接宣言して呼び出すが、lodashなどでより安全なコードを提供することができる.

    リファレンス


    コード時計のYouTube講座
    このゲーム例は、YouTubeリンクを介して直接実施することができる.