The amount of computation caused by frequent callbacks can cause page jitter and even stutter. To circumvent this, we need some means of controlling how often events are triggered. It was in this context that Throttle and Debounce came into being.

The nature of “throttling” and “shaking”

Both of these things exist as closures.

They control the event firing frequency by wrapping the corresponding callback function, caching the time information in the form of free variables, and finally using setTimeout.

Throttle: The first person to rule

Throttle’s central idea is that no matter how many callbacks you trigger in a given period of time, I’ll only recognize the first one and respond when the timer ends.

A passenger just got off the plane and needed a car, so he called the airport’s only shuttle bus to pick him up. The driver drove to the airport, thinking it would be worth it to pick up a few more people. I waited ten minutes. So the driver opened the timer, while beckoning the guests behind off and on. During those 10 minutes, passengers disembarking from the next plane can only take this one bus, and at the end of the 10 minutes, no matter how many unpacked passengers are left behind, the bus must leave.

In this story, the “driver” is our throttle valve. He controls the timing of the departure. The “passenger” is the callback task that keeps pouring in because of our frequent manipulation of events, and it needs to accept the arrangement of the “driver”; The “timer” is the time information in the form of free variables, which is the basis for the “driver” to decide to start. Finally, the “start” action corresponds to the execution of the callback function.

In summary, throttling is achieved by ignoring subsequent callback requests for a period of time. As soon as a guest calls a bus, the driver will start a timer for him. Within a certain time, all the guests who need to take a bus behind them have to queue up to get on the same bus, and no one can get more buses.

The corresponding actual interaction is the same: every time the user triggers the Scroll event, we start the timer for that triggered operation. For a while, all subsequent Scroll events will be treated as “passengers of a car” — they cannot trigger new Scroll callbacks. The first Scroll callback will not be executed until a certain amount of time has elapsed, and any subsequent scroll callback triggered by a certain amount of time will be ignored by the throttle.

With that in mind, let’s now implement a throttle together:

// fn is the event callback we need to wrap, and interval is the threshold of the time intervalfunctionThrottle (fn, interval) {// last indicates the time when the callback was last triggeredletLast = 0 // Return throttle as a functionreturn function() {// Preserve this context when calledletContext = this // Preserves the arguments passed in the callletArgs = arguments // Record the time when the callback is triggeredletNow = +new Date() // Check whether the difference between the time triggered last time and the time triggered this time is smaller than the thresholdif(now-last >= interval) {// If the interval is greater than the interval threshold we set, execute the callback last = now; fn.apply(context, args); Const better_scroll = throttle(() => console.log())'Triggered a scroll event'), 1000)

document.addEventListener('scroll', better_scroll)

Copy the code

Debounce: The last person to call the shots

The central idea is: I will wait for you to the end. No matter how many times you trigger a callback over a given period of time, I only think it’s the last one.

Continue with the driver’s story. This time the driver was more patient. After the first passenger gets on the bus, the driver starts the timer (say ten minutes). If another passenger arrives within 10 minutes, the driver will reset the timer and start waiting for another 10 minutes (delayed). Until a passenger gets on the bus and no new passenger gets on for ten minutes, the driver decides that no one really needs the ride and moves on.

We compare debounce to Throttle: in the logic of Throttle, “the first person is the boss”, it timed only the first passenger and performed a callback when the time was up. According to Debounce, “the last man has the last word”, he sets a new timer for every new passenger.

(1) Debounce

// fn is the event callback we need to wrap, and delay is the wait time for each delayed executionfunctionDebounce (fn, delay) {// TimerletTimer = null // Return debounce as a functionreturn function() {// Preserve this context when calledletContext = this // Preserves the arguments passed in the callletArgs = arguments // Each time an event is triggered, the previous timer is clearedif(timer) {clearTimeout(timer)} // Set a new timer timer =setTimeout(functionConst better_scroll = debounce(() => console.log() {fn.apply(context, args)}, delay)}} const better_scroll = debounce(() => console.log()'Triggered a scroll event'), 1000)

document.addEventListener('scroll', better_scroll)

Copy the code

Optimize Debounce with Throttle

Debounce’s problem was that he was “too patient”. Imagine if the user is doing something so frequently that he doesn’t wait for the delay set by Debounce to expire before the next operation, and debounce regenerates the timer for that user each time, the callback is delayed countless times. Frequent delays can lead to delayed responses and the same “this page is stuck” feeling.

To avoid self-defeating, we need to throttle and create a debounce with a baseline — you can, but I have my rules: I will regenerate the timer for you during the delay; But as soon as the delay time is up, I have to give the user a response. This throttle-debounce approach has been implemented by many mature front-end libraries in their enhanced throttle implementations:

// fn is the event callback we need to wrap, delay is the threshold of the time intervalfunctionThrottle (fn, delay) {// Last indicates the time when the callback is triggered last time, and timer indicates the timerletLast = 0, timer = null // Throttle as a functionreturn function() {// Preserve this context when calledletContext = this // Preserves the arguments passed in the callletArgs = arguments // Record the time when the callback is triggeredletNow = +new Date() // Check whether the difference between the time triggered last time and the time triggered this time is smaller than the thresholdif(now-last < delay) {// If the interval is less than the interval threshold we set, set a new timer for the trigger operation clearTimeout(timer) timer =setTimeout(function () {
          last = now
          fn.apply(context, args)
        }, delay)
    } else{// If the interval exceeds the interval threshold we set, it is not equal, Last = now fn.apply(context, }}} const better_scroll = throttle(() => console.log()'Triggered a scroll event'), 1000)

document.addEventListener('scroll', better_scroll)

Copy the code

summary

Throttle and Debounce are good code snippets that we use in our daily development, and they’re often used in front-end interviews. “Understand the code” and “understand the process” are not enough in this section. The important thing is to write them into your own projects and experience the performance improvements that throttling and anti-shaking can bring.

Please give it a thumbs up