【訳】JavaScriptの中のスロットルと手ぶれ防止(debounce)


目次
  • JavaScriptにおける節流および手ぶれ防止
  • 節流と手振れ防止の違い
  • 使用シーン
  • により、節流と手振れ防止を実現しました.
  • 手ぶれ防止
  • 節流
  • JavaScriptの中の節流と震え防止
    関数を使いすぎて性能に影響がありますか?
    性能の問題を解決するのはJavaScriptの応用の中でいつも直面することです.
    節流と振動防止により、どのWeb開発者も知っておくべき技術です.イベントバインディングイベントハンドラとして機能する.いくつかのシーンでは、関数を呼び出す必要はありません.私達はwindowのresize事件のトリガの時に1つのフィードバックを実行したいですが、ブラウザのウィンドウサイズが変わるたびにリピートを実行するのは本当に意味がありますか?そうではないかもしれません.私たちはユーザーが相互作用を完了したら、再度コールバックを実行したいです.
    ここにはデモがあります.(ウィンドウの中でmousemoveとtouchmoveイベントハンドラを節流と手振れ防止処理します.節流関数の呼び出しは赤いひし形で表されています.手振れ防止関数の呼び出しは緑の円で表されています.)https://codepen.io/jh3y/pen/mGVGvm
    節流と振動防止の違い
    スロットル:
    スロットルは英語の風門、アクセル、スロットルバルブの意味です.私たちはアクセルを踏むことでエンジンに入るオイルの量をコントロールできると考えられます.このような操作のように、関数の呼出回数をコントロールしたい場合があります.
    私たちはもっといい類比を探しています.節流は楽透のように5秒ごとにボールが一つしか出ません.
    もっといい類比はバーでお酒を飲んで、バーに行ったかもしれませんが、ここでは45分ごとにおかわりできる規則があります.最初の分に一杯のお酒を注文してから、従業員が一杯を渡してくれました.それから、一分ごとに一杯を飲みたいですが、社長はあなたにこのようにさせません.45分後に次の一杯をあげます.だから、45分待ってから一杯飲むことができます.
    スロットルを使う時、私達はスロットルが終わる時に最後の呼び出しをすることを望むかもしれません.想像してみてください.15分目に一杯のお酒を注文した後、断られました.45分目には注文していませんでしたが、15分目の注文がありましたので、一杯差し上げました.社長は申し訳ございませんでした.
    流れは一つの関数と一つのタイムアウトパラメータを受信する高次関数である.スロットルエネルギー制御関数は指定された時間間隔で一回だけ呼び出されます.その役割は減速関数の呼び出しです.
    震え防止debounce:**震え防止の説明はちょっと難しいかもしれません.レストランで注文するように、従業員に食べ物の話をします.最後に他のものが必要ですか?これらはもう十分です.他のものはいらないと確定したら、彼女たちは離れてあなたに準備します.もし必要であれば、注文書に物を追加し続けます.最後に彼女たちはまたあなたに他の必要があるかどうかを聞きました.あなたがもう十分だと確信しました.
    手振れ防止と節流が異なり、手振れ防止を使用すると、ユーザが操作を停止するまで関数呼び出しを開始します.ユーザーが入力またはスクロールを完了するときに、計算またはインターフェースを呼び出すシーンでよく使われます.
    一言のまとめ:
    節流は流量を制限して、クリック回数は更に多くて、短い時間の内で効き目があるのも一回だけあります.手の震えを防ぐために、何回も注文し続けましたが、最後の効果しかありません.
    WeChatグループの中には、「節流は妊娠と同じで、しばらくは妊娠するしかない」という人がいます.震え防止は退勤と同じです.新しい仕事があれば歩けません.
    上のCodepenでの節流と手ぶれ防止の可視化のデモは、マウスを動かしたり指を動かしたりすることによって、節流や手ぶれ防止を行うことを理解するのに役立つかもしれません.
    使用シーン
  • ユーザは、連続してクリックする場合には、スロットル
  • を使用することができる.
  • APIが起動するときは、ストリーム
  • を使用する.
  • mousemoveまたはtouchmoveイベントコールバック時に節流
  • を使用する.
  • resizeイベントのフィードバック時に、手ぶれ防止
  • を使用します.
  • scrollイベントのコールバック時に、手ぶれ防止
  • を使用する.
  • の自動保存機能において、保存関数は、手ぶれ防止
  • を使用する.
    各シーンを考えてみましょう.節流は手ぶれ防止用より少ないかもしれません.一般的には、節流を考慮した時に、手ぶれを防ぐのがいいかもしれません.
    節流については、最初の例を見てみましょう.ユーザーは連続的にクリックする時に節流を使うかもしれません.私たちのアプリケーションにはボタンがあります.ボタンを押すと、あるAPIを呼び出します.ユーザは一秒に20回クリックすることができますが、一秒に一回だけフィードバックをトリガします.
    手ぶれ防止には、自動保存機能の例が見られます.ユーザーが更新またはインタラクティブ操作を行うたびに、自動保存関数はアプリケーションの状態を保存しようとします.この時、手ぶれ防止ができます.しばらくの間、ユーザーが更新やインタラクティブ操作をしないで保存します.これにより、ユーザが連続して操作する時に必要以上に保存することができ、性能の向上に有利になります.
    スロットルとブレ止めを実現
    節流と防振は様々な異なる実装があるが、最終的な目標は同じであり、大部分の実現にはsetTimeoutが使われている.
    https://codepen.io/jh3y/pen/opNYWy この例では、関数は2秒で手振れ制限を行い、3秒で節流制限を行う.
    振れを防ぐ
    両者の中で、手ぶれ防止は簡単です.
    const debounce = (func, delay) => {
      let inDebounce
      return function() {
        const context = this //          this
        const args = arguments //           
        clearTimeout(inDebounce) //       
        inDebounce = setTimeout(() => func.apply(context, args), delay) //     ,delay         ,        this    
      }
    }
    
    関数(func)と遅延時間(delay)を手振れ防止関数に伝達します.inDebounceは実行関数のタイマーの参照です.ここではクローズドです.
    もし私たちが第1の呼び出しであれば、関数は遅延終了時(すなわちdelayミリ秒)に実行されます.もし私たちが遅延が終わる前に(つまりdelayミリ秒以内に)再度呼び出したら、再計算して、再計算後のdelayミリ秒で実行します.
    以下は、手ぶれ防止の例を示します.
    debounceBtn.addEventListener('click', debounce(function() {
      console.info('Hey! It is', new Date().toUTCString());
    }, 3000));
    
    この例では、ボタンをクリックして3秒停止すると、時間が印刷されます.
    流れをよくする
    節流の実現はちょっと複雑です.節流の操作には違いがあります.制限実行関数の速度から始めましょう.
    const throttle = (func, limit) => {
      let inThrottle
      return function() {
        const args = arguments
        const context = this
        if (!inThrottle) {
          func.apply(context, args)
          inThrottle = true
          setTimeout(() => inThrottle = false, limit)
        }
      }
    }
    
    初めて私たちの関数を呼び出したらすぐに実行してinThrottleをtrueに設定します.limit時間内に再びスロットル関数を呼び出すと、inThrottleがtrueであるため、元の関数は実行されません.limit時間後、inThrottleをfalseにします.
    limit時間後inThrottleはfalseとなりますので、再起動時は初めての呼び出しのように繰り返します.
    節流の例を使う:
    throttleBtn.addEventListener('click', throttle(function() {
      return console.log('Hey! It is', new Date().toUTCString());
    }, 1000));
    
    最後の呼び出しはどうすればいいですか?このようなシーンでは、divの4つの角をクリックしてマウスを結び付けるmousemoveイベントによってdivの大きさを制御し、計算しすぎないように性能を向上させるために、上のthrottleを使って、制限時間を500 msに設定しました.マウスをドラッグすると速くなります.しかし、上記の実装では、ドラッグを開始したばかりの時に、一次関数をトリガします.マウスの位置からdivサイズを計算します.400 msの終了時にトリガしません.だから、この実現は100%予想の結果を得ることができません.したがって、制限時間内に最後の関数呼び出しを取得して実行する必要があります.
    以下は修正後のスロットル関数です.
    const throttle = (func, limit) => {
        let Timer
        let lastRanTime
        return function() {
            const context = this
            const args = arguments
            if (!lastRanTime) { //      
                func.apply(context, args) //          
                lastRanTime = Date.now() //        
            } else {
                clearTimeout(Timer) //       
                Timer = setTimeout(function() { //          
                    if ((Date.now() - lastRanTime) >= limit) {
                        func.apply(context, args)
                        lastRanTime = Date.now()
                    }
                }, limit - (Date.now() - lastRanTime)) //       limit                
            }
        }
    }
    
    この実装は、最後の呼び出しを取得して実行することを確認し、呼び出し時間(各limit時間帯の最後)も正しいです.最後に呼び出したタイムスタンプを保存して、最後の呼び出しがlimitの範囲内にあるかどうかを判断する変数lastRanTimeによって実現します.また、このスロットル関数が実行されているかどうかを判断するために、lastRanTimeを使用しましたので、以前に実現されたinThrottle変数は必要ありません.
    つまり、手ぶれ防止と節流は、応用の性能を著しく向上させるための知見が必要である.