JS関数のデバウンスとスロットルの詳細分析_基礎知識

韦小宝
リリース: 2017-12-16 13:59:41
オリジナル
1425 人が閲覧しました

この記事では主に、JS 関数のデバウンスとスロットルの知識の詳細な分析と、JS コードの分析について紹介します。JS に興味がある友人は参考として読んでください。

この記事では、スロットリングとデバウンスの基本的な概念から始まり、JS 関数の詳細な分析を行います。

1. スロットルとデバウンスとは何ですか?

倹約。蛇口を締めるだけで水の流れは少なくなりますが、水の流れは止まりません。現実の生活では、水を汲むためにバケツに水を汲む必要がある場合があると想像してください。私たちは、何か他のことをするためにずっとそこに立って待っていたくありません。再び戻ってくるときに、水がバケツにいっぱいになるようにしてください。そうしないと、戻ってくる前に水がいっぱいになり、大量の水を無駄にすることになります。戻ってくるときに水がほぼ満タンになるようにスロットルを下げる必要があります。では、JS にはそのような状況があるのでしょうか? 典型的なシナリオは、画像の遅延読み込みとページの scoll イベントの監視、またはマウスの mousemove イベントの監視です。 Mousemove はマウスが移動するときに使用され、ブラウザによって頻繁にトリガーされるため、対応するイベントが頻繁にトリガーされ (水の流れが速すぎる)、ブラウザーのリソースに多くのオーバーヘッドが発生します。中間処理は不要です。このとき、スロットリングする必要があります。ブラウザーが対応するイベントをトリガーするのを防ぐことはできませんが、イベントを処理するメソッドの実行頻度を減らすことはでき、それによって対応する処理のオーバーヘッドを減らすことができます。

揺れを解消します。私がこの用語を初めて知ったのはおそらく高校の物理でした。スイッチが実際に閉じる前にジッターが発生する場合があります。その場合、対応する小さな電球が点滅しても問題はありません。この時、また目にダメージを受けると厄介です。このページでも、ユーザーがコンテンツを入力しているときに、入力ボックスの 1 つがバックグラウンドで対応する単語をクエリすると、入力イベントが頻繁にトリガーされ、その後、逆方向にクエリが実行されるとします。リクエストが発生すると、ユーザーの入力が完了するまで前のリクエストは冗長である必要があります。ネットワークが遅く、バックグラウンドから返されるデータが遅いと仮定すると、表示される関連単語は最後のリクエストが返されるまで頻繁に変更される可能性があります。このとき、一定時間内に再度入力するかどうかを監視し、再度入力がない場合は入力が完了したものとみなし、リクエストを送信します。まだ入力中のため、リクエストは送信されません。

デバウンスとスロットルは異なります。スロットルは中間処理関数を制限しますが、周波数を下げるだけですが、デバウンスはすべての中間処理関数を除外し、指定された時間内のタスクのみを実行します。

2. JS実装。

これまで多くのことを述べてきましたが、辛抱強く読んでいただきありがとうございます。スロットルとデバウンスを自分で実現する方法を見てみましょう。

スロットリング:


/** 实现思路:
  ** 参数需要一个执行的频率,和一个对应的处理函数,
  ** 内部需要一个lastTime 变量记录上一次执行的时间
  **/
  function throttle (func, wait) {
   let lastTime = null    // 为了避免每次调用lastTime都被清空,利用js的闭包返回一个function确保不生命全局变量也可以
   return function () {
    let now = new Date()
    // 如果上次执行的时间和这次触发的时间大于一个执行周期,则执行
    if (now - lastTime - wait > 0) {
     func()
     lastTime = now
    }
   }
  }
ログイン後にコピー


呼び出し方法を見てみましょう:


// 由于闭包的存在,调用会不一样
let throttleRun = throttle(() => {
  console.log(123)
}, 400)
window.addEventListener('scroll', throttleRun)
ログイン後にコピー


この時点で、ページをめちゃくちゃにスクロールすると、123 が 4 に出力されることがわかります。 00ミリ秒ただし、スロットルを設定しないと連続的に印刷されます。待機パラメータを変更して違いを感じることができます。

しかし、ここで、私たちのスロットルメソッドは不完全です。なぜなら、私たちのメソッドは、イベントの発生時にこのオブジェクトを取得しないからです。また、私たちのメソッドは、このトリガーと最後の実行の時間を判断することによって単純かつ粗雑であるためです。時間間隔は、次のことを決定します。コールバックを実行すると、最後のトリガーが実行できなくなるか、ユーザーの出発間隔が実際に非常に短く実行できず、過失致死が発生するため、メソッドを改善する必要があります。


function throttle (func, wait) {
   let lastTime = null
   let timeout
   return function () {
    let context = this
    let now = new Date()
    // 如果上次执行的时间和这次触发的时间大于一个执行周期,则执行
    if (now - lastTime - wait > 0) {
     // 如果之前有了定时任务则清除
     if (timeout) {
      clearTimeout(timeout)
      timeout = null
     }
     func.apply(context, arguments)
     lastTime = now
    } else if (!timeout) {
     timeout = setTimeout(() => {
      // 改变执行上下文环境
      func.apply(context, arguments)
     }, wait)
    }
   }
  }
ログイン後にコピー


このようにして、私たちのメソッドは比較的完成しており、呼び出しメソッドは以前と同じです。

デバウンス:

デバウンス メソッドはスロットリングと一致しますが、このメソッドはジッターが終了したと判断された後にのみ実行されます。


debounce (func, wait) {
   let lastTime = null
   let timeout
   return function () {
    let context = this
    let now = new Date()
    // 判定不是一次抖动
    if (now - lastTime - wait > 0) {
     setTimeout(() => {
      func.apply(context, arguments)
     }, wait)
    } else {
     if (timeout) {
      clearTimeout(timeout)
      timeout = null
     }
     timeout = setTimeout(() => {
      func.apply(context, arguments)
     }, wait)
    }
    // 注意这里lastTime是上次的触发时间
    lastTime = now
   }
  }
ログイン後にコピー


このとき、先ほどと同じ方法で呼び出してみると、ウィンドウをどれだけスクロールしても、スクロールを止めたときにのみ対応するイベントが実行されることがわかります。

デバウンスとスロットリングは多くの成熟した JS によって実装されており、一般的なアイデアは基本的に次のとおりです。


ネチズンの実装メソッドのコードを共有しましょう:

メソッド 1

1. この実装メソッドのアイデアは理解しやすいです: 50 などの間隔を設定します。ミリ秒、から この時間はベースライン設定 タイマー です。最初のトリガー イベントと 2 番目のトリガー イベントの間の間隔が 50 ミリ秒未満の場合は、このタイマーをクリアして新しいタイマーを設定し、イベントがトリガーされるまでこれを繰り返します。最後の 50 ミリ秒以内に再トリガーはありません。コードは次のとおりです:


function debounce(method){ 
 clearTimeout(method.timer); 
 method.timer=setTimeout(function(){ 
  method(); 
 },50); 
}
ログイン後にコピー


这种设计方式有一个问题:本来应该多次触发的事件,可能最终只会发生一次。具体来说,一个循序渐进的滚动事件,如果用户滚动太快速,或者程序设置的函数节流间隔时间太长,那么最终滚动事件会呈现为一个很突然的跳跃事件,中间过程都被节流截掉了。这个例子举的有点夸张了,不过使用这种方式进行节流最终是会明显感受到程序比不节流的时候“更突兀”,这对于用户体验是很差的。有一种弥补这种缺陷的设计思路。

方法二

2.第二种实现方式的思路与第一种稍有差别:设置一个间隔时间,比如50毫秒,以此时间为基准稳定分隔事件触发情况,也就是说100毫秒内连续触发多次事件,也只会按照50毫秒一次稳定分隔执行。代码如下:


var oldTime=new Date().getTime(); 
var delay=50; 
function throttle1(method){ 
 var curTime=new Date().getTime(); 
 if(curTime-oldTime>=delay){ 
  oldTime=curTime; 
  method(); 
 } 
}
ログイン後にコピー


相比于第一种方法,第二种方法也许会比第一种方法执行更多次(有时候意味着更多次请求后台,即更多的流量),但是却很好的解决了第一种方法清除中间过程的缺陷。因此在具体场景应根据情况择优决定使用哪种方法。

对于方法二,我们再提供另一种同样功能的写法:


var timer=undefined,delay=50; 
function throttle2(method){ 
 if(timer){ 
  return ; 
 } 
 method(); 
 timer=setTimeout(function(){ 
  timer=undefined; 
 },delay); 
}
ログイン後にコピー


以上就是本文的所有内容,希望会给大家带来帮助!!

相关推荐:

js实现仿京东快报向上滚动功能

js事件循环机制示例分析

JS如何测试目标网站的打开响应速度

以上がJS関数のデバウンスとスロットルの詳細分析_基礎知識の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート