JavaScript関数節流の実現
9397 ワード
関数の流れは何ですか?
持続トリガされるイベントについては、間隔時間(n秒)が定められており、期間ごとに一回しか実行できない.
前のページの関数の手ぶれ防止は本編の関数節流と似ていますが、また違っています.
関数の手ぶれ防止とは、イベントがトリガされてからn秒後に実行されるコールバックのことで、このn秒以内に再度トリガされると、カウントダウンを再開します.
両方とも関数の頻繁な呼び出しを防ぐことができます.
違いは、イベントが継続的にトリガされる場合、トリガ時間間隔が所定の待ち時間(n秒)より短い場合、関数の手ぶれ防止の場合、関数はずっと実行を遅らせて、実行されない効果をもたらします. 関数の節流の場合、関数はn秒ごとに一回実行します. 関数の流れの実現
関数の流れの実現には、タイムスタンプによって実現されたり、タイマーによって実現されたりします.
タイムスタンプ
考え方
トリガがあれば、Dateで現在の時間を取得し、前回の時間と比較します.時間差が規定の待ち時間より大きい場合、一回実行できます. ターゲット関数を実行した後、previous値を更新して、「前回」の時間を確保する. でなければ、次のトリガが発生したら比較を続けます. コード
考え方
タイマーで時間間隔を実現します.タイマーが存在しない場合、関数が実行できると説明し、タスクキューにターゲット関数を登録するためのタイマーを定義します. ターゲット関数の実行後、保存タイマID変数を空 に設定します.
は、タイマーが定義されているとき、待ち時間中であることを示している.次のトリガイベントを待ってから確認します. 注① 注①:手ぶれを防ぐためには、すぐにキャンセルして、タイマーを再定義して、時間を計測します.関数による手ぶれ防止(debounce)
コード
期間:タイムスタンプによって実現される:まずターゲット関数を実行し、後は所定の時間帯を待つ. タイマが実現したのは、所定の時間を待ってから実行します. は、トリガを停止した後、タイマーがすでにタスクキューにターゲット関数を登録していると、最後の時間も実行されます. 最適化:両者の結合
両者を結合して、一回のトリガを実現して、二回の実行(先に直ちに実行して、最後にも実行があります)
コード
もう一回の触発を実現しました.二回実行して、効果があります.
問題は、前の周期の「尾」と次の周期の「頭」の間で、時間間隔の制御が失われていることです.
修復
よく見ると、問題は
「直接実行可能」の場合のみ
同時に、導入変数
完全後コード:
undersscoreとmqyqingfengを参考にして、一回目/最後のタイムトライアルの実行を有効にするかどうかを実現します. leading:falseは、第1回実行禁止 を表しています. triling:falseは停止トリガを無効にするコールバック を表しています.
参照コード:
関数ブレ止めで、キャンセル方法を実現しました.同じ理屈:
持続トリガされるイベントについては、間隔時間(n秒)が定められており、期間ごとに一回しか実行できない.
前のページの関数の手ぶれ防止は本編の関数節流と似ていますが、また違っています.
関数の手ぶれ防止とは、イベントがトリガされてからn秒後に実行されるコールバックのことで、このn秒以内に再度トリガされると、カウントダウンを再開します.
両方とも関数の頻繁な呼び出しを防ぐことができます.
違いは、イベントが継続的にトリガされる場合、トリガ時間間隔が所定の待ち時間(n秒)より短い場合、
関数の流れの実現には、タイムスタンプによって実現されたり、タイマーによって実現されたりします.
タイムスタンプ
考え方
トリガがあれば、Dateで現在の時間を取得し、前回の時間と比較します.
function throttle(func, wait) {
let previous = 0;
return function() {
let now = +new Date();
let context = this;
if (now - previous >= wait) {
func.apply(context, arguments);
previous = now; // previous
}
}
}
合図container.onmousemove = throttle(doSomething, 1000);
タイマー考え方
タイマーで時間間隔を実現します.
コード
function throttle(func, wait) {
let time, context
return function(){
context = this
if(!time){
time = setTimeout(function(){
func.apply(context, arguments)
time = null
}, wait)
}
}
}
効果の違い期間:
両者を結合して、一回のトリガを実現して、二回の実行(先に直ちに実行して、最後にも実行があります)
コード
function throttle (func, wait) {
let previous = 0
let context, args, time
return function(){
let now = +new Date()
context = this;
args = arguments
if(now - previous >= wait){ // ,
func.apply(context, args)
previous = now
} else { // ,
if(time) clearTimeout(time)
time = setTimeout(
() => {
func.apply(context, args)
time = null
}
, wait)
}
}
}
問題もう一回の触発を実現しました.二回実行して、効果があります.
問題は、前の周期の「尾」と次の周期の「頭」の間で、時間間隔の制御が失われていることです.
修復
よく見ると、問題は
previous
の設定にあります.「直接実行可能」の場合のみ
previous
値が更新され、タイマによりジョブキューに登録されて実行される場合はprevious
の更新は無視される.previous
の値は、「前回の実行」ではなく、「前回の直接実行可能な場合に実行する」時間となる.同時に、導入変数
remaining
は、まだ待ち時間が必要であることを示し、最後の一回の実行も時間間隔に合致するようにする.完全後コード:
function throttle(func, wait) {
let previous = 0;
let context, args, time, remaining;
return function() {
let now = +new Date();
context = this;
args = arguments;
remaining = wait - (now - previous)//
if (remaining <= 0) {
func.apply(context, args);
previous = now // “ ”
} else {
if (time) {
clearTimeout(time);
}
time = setTimeout(() => {
func.apply(context, args);
time = null;
previous = +new Date() // “ ”
}, remaining) //
}
};
}
さらなる最適化undersscoreとmqyqingfengを参考にして、一回目/最後のタイムトライアルの実行を有効にするかどうかを実現します.
options
を第3のパラメータとして設定し、どの効果が伝達されているかを判断することで約束する.参照コード:
function throttle(func, wait, options) {
let time, context, args, result;
let previous = 0;
if (!options) options = {};
let later = function() {
previous = options.leading === false ? 0 : new Date().getTime();
time = null;
func.apply(context, args);
if (!time) context = args = null;
};
let throttled = function() {
let now = new Date().getTime();
if (!previous && options.leading === false) previous = now;
let remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (time) {
clearTimeout(time);
time = null;
}
previous = now;
func.apply(context, args);
if (!time) context = args = null;
} else if (!time && options.trailing !== false) {
time = setTimeout(later, remaining);
}
};
return throttled;
}
キャンセル機能関数ブレ止めで、キャンセル方法を実現しました.同じ理屈:
...
throttled.cancel = function() {
clearTimeout(time);
time = null;
previous = 0;
}
...
参考:https://github.com/mqyqingfeng/Blog/issues/26