防抖和节流的介绍

219 阅读3分钟

在介绍防抖节流之前,先了解一下定时器和延时器:

1.定时器:setInterval(()+>{},time),定一段时间time,并间隔time重复执行()=>{}

2.延时器:setTimeout(()=>{},time),定一段时间time,time时间过后执行一次()=>{}

1 防抖 Debounce

介绍

​ 当一个事件持续触发时,防抖会等待一段时间,在这段时间之内,事件没有再触发,执行最后一次触发的操作。

场景

​ 在处理搜索框的输入事件,为避免每次输入都发送请求,在等待用户输入完毕后再发送请求。

示例

debounce01.gif

​ 由于输入框发生变化时,onChange()事件就会执行,可以看到,每当输入框里有输入或者删除,立即有日志打印,相当于用户只要在搜索框输入内容,页面就会向服务器发起请求,这样很浪费服务器资源。

debounce02.gif

​ 此时,加入if...语句,可以理解为,防抖 => 防止每次输入内容,都立马执行延时器中的输出日志,而是输入内容停止1s后输出日志。

​ 防抖输入框源码:

@Entry
@Component
struct Index {
  @State tid: number = -1

  build() {
    Column() {
      TextInput()
        //输入内容发生变化时,触发onChange()事件
        .onChange(value => {
          
          if (this.tid != -1) {
            //清除上一次输入操作创建的延时器
            clearTimeout(this.tid) 
          }
          
          this.tid = setTimeout(() => {
            console.log("输入的内容:", value, "延时器id:", this.tid)
          }, 1000)
        })
    }
  }
}

封装防抖函数

需求:防抖函数,参数:传入业务逻辑时间间隔,返回值:返回一个添加了防抖处理的函数。

debounce03.gif

完整代码

//封装防抖函数
const debounce = (func: (value: string) => void, time: number) => {
  let tid = -1
  const newFunc = (value: string) => {
    if (tid != -1) {
      clearTimeout(tid)
    }
    tid = setTimeout(() => {
      func(value)
    }, time)
  }
  return newFunc
}

@Entry
@Component
struct Index {
  onTextChange = debounce((text) => {
    console.log("输入的内容:", text)
  }, 1000)

  build() {
    Column() {
      TextInput()
        .backgroundColor("#ffe0dede")
        .onChange(value => {
          this.onTextChange(value)
        })
    }
  }
}

2 节流 Throttle

介绍

​ 控制在一定的时间间隔内只执行第一次函数(业务),来限制事件的触发次数。

场景

​ 处理滚动事件,限制频繁触发的情况,例如在滚动加载场景中,只有在用户停止滚动一段时间后才加载内容。(用户在第一次滚动触底后,等待一段时间获取到内容,在等待的这段时间内,多次滑动,并不会触发多次请求数据的业务,只有第一次的请求有效。)

示例

Throttle01.gif

​ 滑动触底后,即触发onReachEnd()事件,第一次触底后,延迟3s获取到数据,频繁滑动,在3s之内可以多次请求数据。

Throttle02.gif

​ 加入节流逻辑之后,触底后,在3s内多次滑动到触底,最终只获取到一次数据。

​ 节流获取数据源码:

@Entry
@Component
struct Index {
  @State list: number[] = Array.from({ length: 10 }, (val: undefined, index: number) => index)
  @State isRun: boolean = false
  addList = () => {
    const newList: number[] = Array.from({ length: 10 }, (val: undefined, index: number) => index)
    this.list.push(...newList)
  }

  build() {
    Column() {
      List() {
        ForEach(this.list, (item: number) => {
          ListItem() {
            Text(item.toString())
              .padding(20)
          }
          .height(50)
        })
      }
      .width("100%")
      .height(300)
      .divider({ strokeWidth: 1, color: Color.Red })
      .onReachEnd(() => {

        if (this.isRun) {
          return //直接返回:不执行后续代码,立即结束当前函数或块的执行
        }
        this.isRun = true
        setTimeout(() => {
          this.addList()
          console.log("获取数据")
          //业务做完了
          this.isRun = false
        }, 3000)

      })

    }
  }
}

封装节流函数

需求:节流函数,参数:传入业务逻辑时间间隔,返回值:返回一个添加了节流处理的函数。

//封装节流函数
const throttle = (func: () => void, time: number) => {
  let isRun: boolean = false
  const newFunc = () => {
    if (isRun) {
      return
    }
    isRun = true
    setTimeout(() => {
      func()
      isRun = false
    }, time)
  }
  return newFunc
}

@Entry
@Component
struct Index {
  @State list: number[] = Array.from({ length: 10 }, (val: undefined, index: number) => index)
  addList = () => {
    const newList: number[] = Array.from({ length: 10 }, (val: undefined, index: number) => index)
    this.list.push(...newList)
  }
  
  getData = throttle(() => {
    this.addList()
    console.log("获取数据")
  }, 3000)

  build() {
    Column() {
      List() {
        ForEach(this.list, (item: number) => {
          ListItem() {
            Text(item.toString())
              .padding(20)
          }
          .height(50)
        })
      }
      .width("100%")
      .height(300)
      .divider({ strokeWidth: 1, color: Color.Red })
      .onReachEnd(() => {
        this.getData()
      })
    }
  }
}

更多关于防抖、节流介绍、应用场景可移步https://zhuanlan.zhihu.com/p/655620823