ntscshen / ntscshen.github.io

个人博客
0 stars 2 forks source link

JS经典interview(三) - 节流和防抖 #4

Open ntscshen opened 7 years ago

ntscshen commented 7 years ago

某个技术,一定是解决某些痛点的

一、应用场景

防抖debounce: 输入框搜索 节流throttle: 所有高频点击操作、onresize、window.onresize() 事件、mousemove 事件、上传进度等情况

二、区别

防抖(debounce): 一个函数在某个时间内,无论被触发多少次,都只执行最后一次。假设某个时间是3s,在3s内这个函数无论被触发多少次,3s都会被重新计时,直到3s内当前函数没有任何执行请求。 节流(throttle): 一个函数在某个时间内,只执行一次,无视后来产生的函数调用请求

10s内触发100次

  1. 防抖只执行最后一次函数
  2. 节流(2s)执行5次

三、代码实现


/**
 * 函数防抖
 * @param {function} func 执行函数(二次)
 * @param {number} [delay=1000]
 */
const debounce = (func, delay) => {
  let timeout;
  return () => {
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
      func();
    }, delay);
  };
};
const log = debounce(() => console.log('call'), 2000);
log();
log();
log();

/**
 * 函数节流
 * @param {function} fn 执行函数(二次)
 * @param {number} [wait=50] 等待时间ms(毫秒)
 */
const throttle = (fn, wait = 50) => {
  // 上一次执行 fn 的时间
  let prevTime = 0;
  // 将 throttle 处理结果当作函数返回
  return (...args) => {
    // 获取当前时间,转换成时间戳,单位毫秒
    let nowTime = +new Date();
    // 将当前时间和上一次执行函数的时间进行对比
    // 大于等待时间就把 prevTime 设置为当前时间并执行函数 fn
    if (nowTime - prevTime > wait) {
      prevTime = nowTime;
      fn.apply(this, args);
    }
  };
};

// DEMO
const betterFn = throttle(() => console.log('fn 函数执行了'), 1000);
// 每 10 毫秒执行一次 betterFn 函数,但是只有时间差大于 1000 时才会执行 fn
setInterval(betterFn, 0);
// 用这个对比throttle函数做对比测试
setInterval(() => console.log('fn 函数执行了'), 0);

四、对比lodash源码解析





_.debounce 防抖:在规定的时间内再次触发当前函数,则从0开始计算 使用场景:搜索联想展示下拉框,用户极高频率操作后( input输入 ),触发函数场景 _.throttle 节流:在规定时间内触发内触发一次当前函数 使用场景:滚动,在N秒内必须触发一次函数的场景

性能优化

  • 有时候要对滚动做一些事件监听( 比如滚动到页面底部加载数据、滚动到某个高度显示某条数据 )
  • 频繁的触发监听的回调函数
  • 而且回调中会伴随着DOM操作
  • 继而会大量的引发浏览器的重排和重绘
  • 导致性能问题 优化方案有那些?
ntscshen commented 7 years ago
核心就是限制某一个方法被频繁的触发

限制的方法有两种(两种差别不大)

  1. 一段时间内一个函数被连续调用,让其间隔一段时间触发一次( 这种机制叫"函数节流"Throttle );
  2. 另外一种是,在一段时间内一个函数被连续调用,只让其执行一次( 这种机制叫"函数去抖"debounce );

第一个的应用场景:在 scroll 的时候,滚动到某个高度,显示某个模块。或者在滚动到底部的时候,触发加载"更多"数据 第二个的应用场景:

  1. 浏览器 resize 的时候
  2. 输入验证码的时候( 验证码输入完成后发送请求 - 进行校验 ),不过这块我一般触发 buttonchecked 来做。
ntscshen commented 7 years ago

函数防抖 - debounce

// 《高程三》給了最简洁经典的"防抖"代码
function debounce(method, context) {
    clearTimeout(method.tId);
    method.tId = setTimeout(function(){
        method.call(context);
    }, 100);
}
function log(){
    console.log('Hello debounce');
}
window.onscroll = function() {
    debounce(log);
}

在窗口内滚动一次,停止,100ms后,打印Hello debounce 知道原理和概念了 - 那直接用 Lodash 中的 _.debounce

function log(){
    console.log('Hello debounce');
}
window.onscroll = _.debounce(log, 100);
import { debounce } from 'lodash';
class autocomp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            results: []
        }
        // 要把函数存储在一个不易变动的地方,否则debounce无法生效
       this.handleInputThrottled = debounce(this.handleInput, 100);
    }
    handleInput = evt => {
        const value = evt.target.value
        const filteredRes = data.filter((item)=> {
            // algorithm to search through the `data` array
        })
        this.setState({ results: filteredRes })
    }
    render() {
        let { results } = this.state;
        return (
            <div className='autocomp_wrapper'>
                <input placeholder="Enter your search.." onChange={this.handleInputThrottled} />
                <div>
                    {results.map(result=>{result})}
                </div>
            </div>
        );
    }
}
ntscshen commented 7 years ago

函数节流 - throttle

// 用时间戳来判定是否已经到了回调该执行的时间,记录上次执行的时间戳,
// 每次触发 scroll 事件执行回调,在回调中判断当前时间戳距离上次执行
// 时间戳的间隔是否达到指定时间。如果是,则执行,并跟新上次执行的时间戳。

function log(){
    console.log('Hello debounce');
}

var throttleV2 = function (fn, mustRunDelay) {
    var timer = null;
    var t_start;
    return function () {
        var _self = this;
        var t_curr = +new Date();
        if (!t_start) {
            t_start = t_curr;
        }
        if (t_curr - t_start >= mustRunDelay) {
            fn.apply(_self, arguments);
            t_start = t_curr;
        }
    };
};

window.onscroll = throttleV2(log, 500);

// window.onscroll = _.throttle(log, 500);

知道原理和概念 - 那直接用 Lodash 中的 _.throttle

Read more

JavaScript 函数节流和函数去抖应用场景辨析