仓颉探索-开发一个debounce throttle工具

151 阅读3分钟

debounce throttle 简介

具体的相关概念可以看我之前写的文章。这里我们只简单的介绍

鸿蒙应用开发-JS中节流和防抖

debounce 防抖

在事件被触发n秒后去执行回调函数。如果n秒内该事件被重新触发则重新计时。就将频繁触发的事件合并为一次,且在最后执行

直白的说就是,给定一个定长的滑动的时间段,如果两次事件之间的时间差小于这个时间段,就从最新的时间点重新计时这个时间段,直到对应时间段之内没有事件才执行实际的回调

image.png

throttle 节流

限制函数的执行频率,保证一定时间内只执行一次函数调用。无论触发频率多高,都会在指定时间间隔内执行一次函数

直白的说就是,给定一个定长的时间段,判断两次事件之间的时间差,如果大于这个时间段就执行实际的回调, 否则不做处理

image.png

设计思路

  1. 仓颉提供了定时器的API,借用这个API可以同时实现throttle和debounce的功能
  2. 仓颉是编译型语言,它提供了宏可以帮助我们在编译器生成代码,我们可以使用宏实现js中装饰器的能力
  3. 仓颉的Timer和iOS中的Timer一样,受到运行时以及Timer创建耗时的影响,不一定准确,会有一些偏差。

定义模式

借助仓颉中的枚举,可以轻而易举的实现模式 RepeaterMode 的定义。同时仓颉枚举和Swift中的枚举一样,可以携带参数,我们可以利用这个参数来定义throttle和debounce的时间段

定义 RepeaterConfig 方便直接使用它里面的成员变量。同样的,利用仓颉中的成员属性,可以返回对应的config对象,RepeaterConfig对象使用主构造函数构造,又省了不少代码

/**
 * 模式,时间为毫秒
 */
public enum RepeaterMode {
    | Leading(Int)
    | Trailing(Int)
    | Both(Int)

    public prop config: RepeaterConfig {
        get() {
            match (this) {
                case Leading(interval) => RepeaterConfig(
                    interval: Duration.millisecond * interval,
                    leading: true,
                    trailing: false
                )
                case Trailing(interval) => RepeaterConfig(
                    interval: Duration.millisecond * interval,
                    leading: false,
                    trailing: true
                )
                case Both(interval) => RepeaterConfig(
                    interval: Duration.millisecond * interval,
                    leading: true,
                    trailing: true
                )
            }
        }
    }
}

/**
 * 属性配置
 */
public struct RepeaterConfig {
    /**
     * interval 等待间隔时间(毫秒)
     * leading在开头调用
     * trailing在最后调用
     */
    public RepeaterConfig(
        var interval!: Duration,
        var leading!: Bool = false,
        var trailing!: Bool = true
    ) {}
}

定义Repeater

定义一个Repeater类来实现throttle和debounce的功能。该类提供两个构造函数,既可以接收mode也可以接收config,方便通过宏@Debounce和@Throttle使用以及直接通过类Repeater使用

public class Repeater {
    private var lastCallTime: MonoTime = getNowTime()
    private var lastRunTaskTime: MonoTime = getNowTime()
    private var firstRunTask: Bool = false
    // 定时器,只有trailing才会使用
    private var timer: ?Timer = None

    // 配置和任务
    private let config: RepeaterConfig
    private let task: RepeaterTask
    private let throttle: Bool

    public init(throttle!: Bool, mode!: RepeaterMode, task!: RepeaterTask) {
        this(throttle: throttle, config: mode.config, task: task)
    }

    public init(throttle!: Bool, config!: RepeaterConfig, task!: RepeaterTask) {
        this.config = config
        this.task = task
        this.throttle = throttle
        this.verifyConfig()
    }
    ...
}

使用方式

就像上面我们说的,我们既可以通过Repeater类来使用,也可以通过提供的宏来使用

通过Repeater类

let start = getNowTime()
let debouncer = Repeater(throttle: false, mode: Trailing(100)) {
    println('testDebounce执行间隔 ${getNowTime()-start}')
}

debouncer.call()

通过宏

class RepeaterMemberMacro {
    let start = getNowTime()

    @Debounce[100]
    func myDebounce() {
        println('myDebounce执行间隔 ${getNowTime()-start}')
    }

    @Throttle[50]
    func myThrottle() {
        println('myThrottle执行间隔 ${getNowTime()-start}')
    }
}

工具地址

gitcode.com/unravel/cj_…

欢迎大家体验完善

参考资料

  1. 仓颉语言官网
  2. 仓颉宏简介
  3. 仓颉包管理工具-cjpm
  4. 函数重载
  5. 鸿蒙应用开发-JS中节流和防抖