ホームページ  >  記事  >  ウェブフロントエンド  >  JS関数のdebounceとは何なのか詳しく解説!

JS関数のdebounceとは何なのか詳しく解説!

亚连
亚连オリジナル
2018-06-22 15:54:021340ブラウズ

この記事では主に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)

この時点で、ページを狂ったようにスクロールすると、400 ミリ秒で 123 が出力されることがわかります。スロットリングがなければ、出力され続けます。待機パラメータを変更すると、気分が変わります。

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

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); 
}

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在vue-cli中如何实现移动端自适应

在Webpack中如何加载SVG

webpack打包配置(详细教程)

在Javascript中自适应处理方法

以上がJS関数のdebounceとは何なのか詳しく解説!の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。