JS-防抖-节流

156 阅读2分钟

认识防抖和节流函数

image.png

防抖debounce函数

image.png

案例

image.png

节流throttle函数

image.png

案例

image.png

生活中的例子

image.png

需求

image.png

Underscore库的介绍

image.png

防抖

  • image.png

index.html

  • <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <input type="text" />
        <button id="cancel">取消</button>
    
        <!-- <script src="01_debounce-this+args.js"></script> -->
        <!-- <script src="./02_debounce-立即执行.js"></script> -->
        <!-- <script src="./03_debounce-取消.js"></script> -->
        <!-- <script src="./04_debounce-函数返回值.js"></script> -->
        <script src="./05_debounce-异常捕获.js"></script>
    
        <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script>
        <script>
          const inputEl = document.querySelector("input");
          let counter = 0;
          const inputChange = function (event) {
            console.log(`发送了第${counter++}次网络请求`, this, event);
            // throw new Error('error~')
            return "aaa";
          };
    
          // inputEl.oninput = inputChange
    
          // 防抖
          // 1.第三方库underscore
          // inputEl.oninput = _.debounce(inputChange, 2000)
          // 2.自己封装
          const debounceChange = debounce(inputChange, 2000, false, (res) => {
            console.log('函数返回值:', res);
          });
          // inputEl.oninput = debounceChange;
          const tempCallback = (...args) => {
            debounceChange.apply(inputEl, args).then(res => {
              console.log('promise的返回值:', res);
            }).catch(err => console.log('捕获错误:', err))
          }
          inputEl.onclick = tempCallback
    
          // 节流
          // 1.第三方库underscore
          // inputEl.oninput = _.throttle(inputChange, 2000)
    
          // 取消功能
          const cancelBtn = document.getElementById("cancel");
          cancelBtn.onclick = function () {
            debounceChange.cancel();
          };
    
        </script>
      </body>
    </html>
    

this,args

  • function debounce(fn, delay) {
        // 1.定义定时器,保存上一次的定时器
        let timer = null;
    
        // 2.真正执行的函数
        const _debounce = function (...args) {
            // 3.取消上一次的定时器
            if (timer) {
                clearTimeout(timer);
            }
            // 延迟执行
            timer = setTimeout(() => {
                // 执行外部函数
                fn.apply(this, args);
            }, delay);
        };
        return _debounce;
    }
    

立即执行

  • function debounce(fn, delay, immediate = false) {
        // 1.定义定时器,保存上一次的定时器
        let timer = null;
        // 判断之前是否执行过
        let isInvoke = false
    
        // 2.真正执行的函数
        const _debounce = function (...args) {
            // 3.取消上一次的定时器
            if (timer) {
                clearTimeout(timer);
            }
    
            // 判断是否需要立即执行
            if (immediate && !isInvoke) {
                // 立即执行
                fn.apply(this, args);
                isInvoke = true
            } else {
                // 延迟执行
                timer = setTimeout(() => {
                    // 执行外部函数
                    fn.apply(this, args);
                    // 执行完一次后,在下次执行前可以再次立即执行
                    isInvoke = false
                }, delay);
            }
        };
    
        return _debounce;
    }
    

取消功能

  • function debounce(fn, delay, immediate = false) {
        // 1.定义定时器,保存上一次的定时器
        let timer = null;
        // 判断之前是否执行过
        let isInvoke = false
    
        // 2.真正执行的函数
        const _debounce = function (...args) {
            // 3.取消上一次的定时器
            if (timer) {
                clearTimeout(timer);
            }
    
            // 判断是否需要立即执行
            if (immediate && !isInvoke) {
                // 立即执行
                fn.apply(this, args);
                isInvoke = true
            } else {
                // 延迟执行
                timer = setTimeout(() => {
                    // 执行外部函数
                    fn.apply(this, args);
                    // 执行完一次后,在下次执行前可以再次立即执行
                    isInvoke = false
                    timer = null
                }, delay);
            }
        };
    
        // 封装取消功能
        _debounce.cancel = function () {
            if (timer) {
                clearTimeout(timer)
                timer = null
                isInvoke = false
            }
        }
    
        return _debounce;
    }
    

函数返回值

  • function debounce(fn, delay, immediate = false, resultCallback) {
        // 1.定义定时器,保存上一次的定时器
        let timer = null;
        // 判断之前是否执行过
        let isInvoke = false;
    
        // 2.真正执行的函数
        const _debounce = function (...args) {
            return new Promise((resolve, reject) => {
                // 3.取消上一次的定时器
                if (timer) {
                    clearTimeout(timer);
                }
    
                // 判断是否需要立即执行
                if (immediate && !isInvoke) {
                    // 立即执行
                    const result = fn.apply(this, args);
                    // 1.将函数返回值作为回调函数参数返回
                    if (resultCallback && typeof resultCallback === "function") {
                        resultCallback(result);
                    }
                    // 2.将函数返回值通过resolve参数返回
                    resolve(result);
    
                    isInvoke = true;
                } else {
                    // 延迟执行
                    timer = setTimeout(() => {
                        // 执行外部函数
                        const result = fn.apply(this, args);
                        // 1.将函数返回值作为回调函数参数返回
                        if (resultCallback && typeof resultCallback === "function") {
                            resultCallback(result);
                        }
                        // 2.将函数返回值通过resolve参数返回
                        resolve(result);
                        // 执行完一次后,在下次执行前可以再次立即执行
                        isInvoke = false;
                        timer = null;
                    }, delay);
                }
            });
        };
    
        // 封装取消功能
        _debounce.cancel = function () {
            if (timer) {
                clearTimeout(timer);
                timer = null;
                isInvoke = false;
            }
        };
    
        return _debounce;
    }
    

异常捕获

  • function debounce(fn, delay, immediate = false, resultCallback) {
        // 1.定义定时器,保存上一次的定时器
        let timer = null;
        // 判断之前是否执行过
        let isInvoke = false;
    
        // 2.真正执行的函数
        const _debounce = function (...args) {
            return new Promise((resolve, reject) => {
                // 3.取消上一次的定时器
                if (timer) {
                    clearTimeout(timer);
                }
    
                // 判断是否需要立即执行
                if (immediate && !isInvoke) {
                    try {
                        // 立即执行
                        const result = fn.apply(this, args);
                        // 1.将函数返回值作为回调函数参数返回
                        if (resultCallback && typeof resultCallback === 'function') {
                            resultCallback(result);
                        }
                        // 2.将函数返回值通过resolve参数返回
                        resolve(result);
                    } catch (err) {
                        reject(err);
                    }
    
                    isInvoke = true;
                } else {
                    // 延迟执行
                    timer = setTimeout(() => {
                        try {
                            // 执行外部函数
                            const result = fn.apply(this, args);
                            // 1.将函数返回值作为回调函数参数返回
                            if (resultCallback && typeof resultCallback === 'function') {
                                resultCallback(result);
                            }
                            // 2.将函数返回值通过resolve参数返回
                            resolve(result);
                        } catch (err) {
                            reject(err);
                        }
                        // 执行完一次后,在下次执行前可以再次立即执行
                        isInvoke = false;
                        timer = null;
                    }, delay);
                }
            });
        };
    
        // 封装取消功能
        _debounce.cancel = function () {
            if (timer) {
                clearTimeout(timer);
                timer = null;
                isInvoke = false;
            }
        };
    
        return _debounce;
    }
    

节流

  • image.png

index.html

  • <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <input type="text" />
        <button id="cancel">取消</button>
    
        <!-- <script src="01_throttle-基本实现.js"></script> -->
        <!-- <script src="02_throttle-leading.js"></script> -->
        <!-- <script src="03_throttle-trailing.js"></script> -->
        <!-- <script src="04_throttle-this+args.js"></script> -->
        <!-- <script src="05_throttle-cancel.js"></script> -->
        <!-- <script src="06_throttle-函数返回值.js"></script> -->
        <script src="07_throttle-异常捕获.js"></script>
    
        <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script>
    
        <script>
          const inputEl = document.querySelector("input");
          let counter = 0;
          const inputChange = function (event) {
            console.log(`发送了第${counter++}次网络请求`, this, event);
            // throw new Error("error~");
            return "aaa";
          };
    
          // inputEl.oninput = inputChange
    
          // 节流
          // 1.第三方库underscore
          // inputEl.oninput = _.throttle(inputChange, 2000)
    
          // 2.自己封装
          const _throttle = throttle(inputChange, 2000, {
            leading: false,
            trailing: true,
            resultCallback: function (res) {
              console.log("resultCallback:", res);
            },
          });
          // inputEl.oninput = _throttle;
          const tempCallback = (...args) => {
            _throttle.apply(inputEl, args)
              .then((res) => {
                console.log("promise的返回值:", res);
              })
              .catch((err) => console.log(err));
          };
          inputEl.oninput = tempCallback;
    
          // 取消功能
          const cancelBtn = document.querySelector("#cancel");
          cancelBtn.onclick = function () {
            _throttle.cancel();
          };
        </script>
      </body>
    </html>
    

leading功能

  • function throttle(fn, interval, options = { leading: true, trailing: fasle }) {
        // 1.记录上一次开始时间
        const { leading, trailing } = options;
        let lastTime = 0;
        // 2.事件触发时,真正执行的函数
        const _throttle = function () {
            // 2.1.获取当前事件触发的时间
            const nowTime = new Date().getTime();
            if (!lastTime && !leading) {
                lastTime = nowTime
            }
            // 2.2.计算剩余多长时间去触发函数
            const remainTime = interval - (nowTime - lastTime);
            if (remainTime <= 0) {
                // 2.3.真正执行函数
                fn();
                // 2.4.保留上次触发的时间
                lastTime = nowTime;
            }
        };
    
        return _throttle;
    }
    

trailing功能

  • function throttle(fn, interval, options = { leading: true, trailing: fasle }) {
        // 1.记录上一次开始时间
        const { leading, trailing } = options;
        let lastTime = 0;
        let timer = null
        // 2.事件触发时,真正执行的函数
        const _throttle = function () {
            // 2.1.获取当前事件触发的时间
            const nowTime = new Date().getTime();
            if (!lastTime && !leading) {
                lastTime = nowTime
            }
            // 2.2.计算剩余多长时间去触发函数
            const remainTime = interval - (nowTime - lastTime);
            if (remainTime <= 0) {
                if (timer) {
                    clearTimeout(timer)
                    timer = null
                }
                // 2.3.真正执行函数
                fn();
                // 2.4.保留上次触发的时间
                lastTime = nowTime;
                return
            }
    
            if (trailing && !timer) {
                timer = setTimeout(() => {
                    timer = null
                    lastTime = !trailing ? 0 : new Date().getTime()
                    fn()
                }, remainTime);
            }
        };
    
        return _throttle;
    }
    

this,args

  • function throttle(fn, interval, options = { leading: true, trailing: fasle }) {
        // 1.记录上一次开始时间
        const { leading, trailing } = options;
        let lastTime = 0;
        let timer = null
    
        // 2.事件触发时,真正执行的函数
        const _throttle = function (...args) {
    
            // 2.1.获取当前事件触发的时间
            const nowTime = new Date().getTime();
            if (!lastTime && !leading) {
                lastTime = nowTime
            }
    
            // 2.2.计算剩余多长时间去触发函数
            const remainTime = interval - (nowTime - lastTime);
            if (remainTime <= 0) {
                if (timer) {
                    clearTimeout(timer)
                    timer = null
                }
    
                // 2.3.真正执行函数
                fn.apply(this, args);
    
                // 2.4.保留上次触发的时间
                lastTime = nowTime;
    
                return
            }
    
            if (trailing && !timer) {
                timer = setTimeout(() => {
                    timer = null
                    lastTime = !trailing ? 0 : new Date().getTime()
                    fn.apply(this, args)
                }, remainTime);
            }
        };
    
        return _throttle;
    }
    

取消功能

  • function throttle(fn, interval, options = { leading: true, trailing: fasle }) {
        // 1.记录上一次开始时间
        const { leading, trailing } = options;
        let lastTime = 0;
        let timer = null
    
        // 2.事件触发时,真正执行的函数
        const _throttle = function (...args) {
    
            // 2.1.获取当前事件触发的时间
            const nowTime = new Date().getTime();
            if (!lastTime && !leading) {
                lastTime = nowTime
            }
    
            // 2.2.计算剩余多长时间去触发函数
            const remainTime = interval - (nowTime - lastTime);
            if (remainTime <= 0) {
                if (timer) {
                    clearTimeout(timer)
                    timer = null
                }
    
                // 2.3.真正执行函数
                fn.apply(this, args);
    
                // 2.4.保留上次触发的时间
                lastTime = nowTime;
    
                return
            }
    
            if (trailing && !timer) {
                timer = setTimeout(() => {
                    timer = null
                    lastTime = !trailing ? 0 : new Date().getTime()
                    fn.apply(this, args)
                }, remainTime);
            }
        };
    
        _throttle.cancel = function() {
            if (timer) {
                clearTimeout(timer)
            }
            timer = null
            lastTime = 0
        }
    
        return _throttle;
    }
    

函数返回值

  • function throttle(fn, interval, options = { leading: true, trailing: fasle }) {
        // 1.记录上一次开始时间
        const { leading, trailing, resultCallback } = options;
        let lastTime = 0;
        let timer = null;
    
        // 2.事件触发时,真正执行的函数
        const _throttle = function (...args) {
            return new Promise((resolve, rejecte) => {
                // 2.1.获取当前事件触发的时间
                const nowTime = new Date().getTime();
                if (!lastTime && !leading) {
                    lastTime = nowTime;
                }
    
                // 2.2.计算剩余多长时间去触发函数
                const remainTime = interval - (nowTime - lastTime);
                if (remainTime <= 0) {
                    if (timer) {
                        clearTimeout(timer);
                        timer = null;
                    }
    
                    // 2.3.真正执行函数
                    const result = fn.apply(this, args);
                    if (resultCallback) {
                        resultCallback(result);
                    }
                    resolve(result);
    
                    // 2.4.保留上次触发的时间
                    lastTime = nowTime;
    
                    return;
                }
    
                if (trailing && !timer) {
                    timer = setTimeout(() => {
                        timer = null;
                        lastTime = !trailing ? 0 : new Date().getTime();
    
                        const result = fn.apply(this, args);
                        if (resultCallback) {
                            resultCallback(result);
                        }
                        resolve(result);
                    }, remainTime);
                }
            });
        };
    
        _throttle.cancel = function () {
            if (timer) {
                clearTimeout(timer);
            }
            timer = null;
            lastTime = 0;
        };
    
        return _throttle;
    }
    

异常捕获

  • function throttle(fn, interval, options = { leading: true, trailing: fasle }) {
        // 1.记录上一次开始时间
        const { leading, trailing, resultCallback } = options;
        let lastTime = 0;
        let timer = null;
    
        // 2.事件触发时,真正执行的函数
        const _throttle = function (...args) {
            return new Promise((resolve, rejecte) => {
                // 2.1.获取当前事件触发的时间
                const nowTime = new Date().getTime();
                if (!lastTime && !leading) {
                    lastTime = nowTime;
                }
    
                // 2.2.计算剩余多长时间去触发函数
                const remainTime = interval - (nowTime - lastTime);
                if (remainTime <= 0) {
                    if (timer) {
                        clearTimeout(timer);
                        timer = null;
                    }
    
                    try {
                        // 2.3.真正执行函数
                        const result = fn.apply(this, args);
                        if (resultCallback) {
                            resultCallback(result);
                        }
                        resolve(result);
                    } catch (err) {
                        rejecte(err);
                    }
    
                    // 2.4.保留上次触发的时间
                    lastTime = nowTime;
    
                    return;
                }
    
                if (trailing && !timer) {
                    timer = setTimeout(() => {
                        timer = null;
                        lastTime = !trailing ? 0 : new Date().getTime();
    
                        try {
                            const result = fn.apply(this, args);
                            if (resultCallback) {
                                resultCallback(result);
                            }
                            resolve(result);
                        } catch (err) {
                            rejecte(err);
                        }
                    }, remainTime);
                }
            });
        };
    
        _throttle.cancel = function () {
            if (timer) {
                clearTimeout(timer);
            }
            timer = null;
            lastTime = 0;
        };
    
        return _throttle;
    }