Anti-shake and throttling should strictly be considered performance optimization knowledge, but in practice they are encountered quite frequently and can cause browsers to freeze if handled improperly or left unchecked. So it is necessary to master early. (Trust me, this will make sense.)

Start with the example of scrollbar listening

Let’s start with a common feature. Many sites offer a button that goes back to the top.

The button will only appear after scrolling a certain distance from the top, so we now abstract the function requirement — listen for the browser scroll event and return the current scroll distance from the top.

The function showTop () {var scrollTop = document. The body. The scrollTop | | document. The documentElement. ScrollTop; Console. log(' scrollbar position: '+ scrollTop); } window.onscroll = showTopCopy the code

But! There is a problem at runtime: the default execution frequency of this function, too! High! ! . To what extent? In Chrome, for example, you can select a page by clicking the scroll bar, then click the Down arrow on the keyboard once, and see that the function executes 8 or 9 times!

However, we don’t really need such high frequency feedback, after all, browser performance is limited and should not be wasted here, so let’s discuss how to optimize this scenario.

Image stabilization (debounce)

Based on the above scenario, the first idea is proposed: when the event is triggered for the first time, the function is not executed immediately, but a deadline value is given, such as 200ms, and then:

  • If the scroll event is not triggered again within 200ms, the function is executed
  • If the scroll event is triggered again within 200ms, the current timer is cancelled and the timer is restarted

Effect: If the same event is fired many times in a short period of time, the function is executed only once.

SetTimeout: setTimeout: setTimeout: setTimeout: setTimeout: setTimeout ()

/* * fn [function] delay [number] delay, Return function() {if(timer){clearTimeout(timer) */ function debounce(fn,delay){let timer = null // Enter the branch statement, indicating that a timing process is currently underway and the same event is triggered again. Timer = setTimeout(fn,delay)}else{timer = setTimeout(fn,delay)}Copy the code

Of course, the above code is to fit the idea, convenient to understand (so intimate not to give a praise? Time = setTimeout(fn,delay);

/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * simplified line * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / function debounce (fn, delay) {let the timer = null Return function() {if(timer){clearTimeout(timer)} timer = setTimeout(fn,delay)} ShowTop () {var scrollTop = document. The body. The scrollTop | | document. The documentElement. The scrollTop; Console. log(' scrollbar position: '+ scrollTop); } window.onscroll = debounce(showTop,1000)} window.onscroll = debounce(showTop,1000Copy the code

At this point, you will find that the scrollbar position will be printed only after stopping scrolling for 1 second.

At this point, anti-shake is implemented, now give the definition:

  • For events that occur in succession over a short period of time (the rolling event above), the idea is to have the event handler execute only once for a certain time period (1000 ms above).

The throttle (throttle)

Thinking further, the result of using the above anti-shake scheme to deal with the problem is:

  • If a scrolling event is triggered over and over again for a limited period of time (for example, if a user is idle and drags the scroll around), it will theoretically never output the current distance from the top as long as the trigger is not stopped.

But what if the product student’s expectation is that even if the user keeps dragging the scrollbar, there will be feedback after a certain interval? (No matter which scheme is more appropriate, we will consider how to achieve it now that the product father has spoken)

It’s pretty simple: we can design a function that opens periodically, like a control valve, that runs once, shuts down for a certain period of time, and then reactivates after that period (similar to a cooldown).

Effect: If a large number of the same events are fired in a short period of time, the function stops working for a specified period of time after it executes once, and does not take effect again until that time has passed.

Use setTimeout to do a simple implementation, plus a status bit valid to indicate whether the current function is working:

function throttle(fn,delay){ let valid = true return function() { if(! Valid){// Rest time no call return false} // Work time, execute the function and set the status bit to invalid during the interval valid = false setTimeout(() => {fn() valid = true; }, delay)}} /* Note that the throttling function is not implemented in the above way. For example, setTimeout can be completely dispensed with. You can replace the status bit with a timestamp, and then determine if the timestamp difference is greater than the specified interval. The return mark of setTimeout can also be directly taken as the judgment condition -- to determine whether the current timer exists. If it exists, it indicates that it is still cooling down, and eliminating the timer after fn execution means activation. The function principle is the same * / / / the following those showTop () {var scrollTop = document. The body. The scrollTop | | document. DocumentElement. ScrollTop; Console. log(' scrollbar position: '+ scrollTop); } window.onscroll = throttle(showTop,1000)Copy the code

The result of running the above code is:

  • If you keep dragging the scroll bar, it will continuously output the distance between the current position and the top at an interval of 1s

Examples of other application scenarios

With these two tips in mind, here are some common development scenarios:

  1. Search box input events, for example, to support input real-time search, you can use throttling scheme (interval of a period of time must query the relevant content), or achieve input interval greater than a certain value (such as 500ms), as the user input is completed, and then start the search, the specific use of which scheme depends on business requirements.
  2. The page resize event is usually used when page adaptation is needed. Dom rendering is required based on the final rendering of the page (this is usually done using anti-shake, since only the last change is needed)

Thinking summary

The above content is based on the core idea of anti-shake and throttling design a simple implementation algorithm, but does not represent the actual library (such as undercore JS) source code is directly like this, at least can be seen, in the above code implementation, because showTop itself is very simple, without considering scope and parameter transfer, So you don’t even use apply, and you actually have to think about passing argument and context (after all, apply uses this object). This knowledge is also covered in this column’s Currification and This Object articles. This article still insists on highlighting the core code and stripping irrelevant function points as far as possible, so it will not be repeated.

The author segmentfault.com/a/119000001… Xiaobian study notes