[Combine 开发] Controlling timing 时间控制

Combine框架里,关于时间控制大致有debounce、delay、measureInterval、throttle、timeout
下面我们分别介绍他们的区别和使用方法
值的我们注意的是Combine中的pipline是异步流,所以这些时间控制的Operator还是很强大的。
 

debounce

在某些情况下,我们可能会面临事件产生过于频繁的问题,比如用户输入的搜索关键字。如果我们在用户每次输入一个字符时都进行搜索,可能会导致不必要的网络请求。这时候,我们可以使用debounce来确保只有在用户停止输入一段时间后才进行搜索。 

let publisher = PassthroughSubject()

// 使用debounce操作符
let debounceCancellable = publisher
    .debounce(for: .seconds(3), scheduler: DispatchQueue.main)
    .sink { value in
        print("Debounce: \(value)")
    }

publisher.send(1)
// 等待一段时间
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    publisher.send(2)
}
publisher.send(3)

// 输出结果:
// Debounce: 2

 时间轴

  • 首先设置了时间窗口的时长,上图为3秒
  • publisher每次发送一个新的数据,都会重新开启一个时间窗口,并取消之前的时间窗口
  • 最后开启的时间窗口的时间结束后,如果没有新的数据,debounce把数据发送到下游

常见用例

  • 搜索框输入:如上例所示,确保在用户停止输入一段时间后再进行搜索,以减少网络请求次数。
  • 用户输入验证:在用户输入时,例如密码或用户名,使用debounce可以确保在用户停止输入后再执行验证,避免频繁的验证操作。
  • UI界面更新:在某些情况下,当界面上的某个状态发生变化时,使用debounce可以避免过于频繁地更新UI,提高性能。

delay 

在某些情况下,我们希望在接收到事件后等待一段时间再进行处理。例如,在用户点击一个按钮后,我们可能希望延迟一段时间再执行相应的操作,以提供更好的用户体验。

let publisher = PassthroughSubject()

// 使用debounce操作符
let debounceCancellable = publisher
    .delay(for: .seconds(3), scheduler: DispatchQueue.main)
    .sink { value in
        print("delay: \(value)")
    }

publisher.send(1)
// 等待一段时间
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    publisher.send(2)
}
publisher.send(3)

// 输出结果:
// delay: 1
// delay: 3
// delay: 2

 时间轴

  • 首先设置了时间窗口的时长,上图为3秒
  • 只有一个时间窗口,它能够让pipline在收到publisher发送的数据后,等待一定的时长,然后再发送数据到下游

常见用例

  • 搜索框输入:如上例所示,确保在用户停止输入一段时间后再进行搜索,以减少网络请求次数。
  • 用户输入验证:在用户输入时,例如密码或用户名,使用debounce可以确保在用户停止输入后再执行验证,避免频繁的验证操作。
  • UI界面更新:在某些情况下,当界面上的某个状态发生变化时,使用debounce可以避免过于频繁地更新UI,提高性能。

measureInterval 

在某些情况下,我们可能需要知道两个事件之间经过了多长时间,以便根据时间间隔执行不同的操作。例如,在用户进行某个操作后,我们可能希望在一段时间内等待,然后执行一些额外的逻辑。这时候,measureInterval就能派上用场。

private var cancellables: Set = []
private var lastClickTime: Double = CFAbsoluteTimeGetCurrent()

func simulateButtonClick() {
    Just(())
        .measureInterval(using: DispatchQueue.main)
        .sink { timeInterval in
            
            print(CFAbsoluteTimeGetCurrent() - lastClickTime)
            lastClickTime = CFAbsoluteTimeGetCurrent()
        }
        .store(in: &cancellables)
}

simulateButtonClick()
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    simulateButtonClick()
}

// 输出结果:
// 0.0002 
// 2.1025  

measureInterval它能够记录publisher发送数据的间隔时间

常见用例

  • 点击事件时间间隔监测:在用户点击按钮或进行其他交互时,使用measureInterval可以监测两次事件之间的时间间隔,从而触发不同的操作。
  • 用户活跃度追踪:记录用户活跃度并定期检查时间间隔,以执行相关的统计或分析。
  • 定时任务:测量两次定时任务触发之间的时间,以确保它们按照预期的间隔执行。

 timeout 

在异步操作中,有时我们希望在一定时间内得到结果,如果超时就认为操作失败。例如,网络请求超过一定时间没有响应,我们可能希望取消请求并提示用户。

enum MyError: Error {
    case timeout
}

let publisher = PassthroughSubject()

// 使用debounce操作符
let cancellable = publisher
    .setFailureType(to: MyError.self)
    .timeout(.seconds(1), scheduler: DispatchQueue.main,customError: { MyError.timeout })
    .sink(receiveCompletion: { error in
        switch error {
        case .finished:
            print("finished")
        case .failure(let failure):
            print("failure: \(failure)")
        }
    }, receiveValue: { value in
        print("send: \(value)")
    })

publisher.send(1)

 等待一段时间
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
    publisher.send(2)
}

// 输出结果:
// send: 1
// failure: timeout

 时间轴

  • 首先设置了时间窗口的时长,上图为1秒
  • publisher.timeout() 的时候,seconds(1)这个超时时间就已经开始计时,而不是从publisher.send开始计时
  • publisher每次发送一个新的数据,都会重新开启一个时间窗口(当然receiveCompletion执行了,整个pipline也就结束了)

常见用例

  • 网络请求超时处理:设置网络请求的超时时间,确保及时处理请求的超时情况。
  • 用户操作限时处理:在某些情况下,限定用户在一定时间内完成某个操作,超时则取消操作。
  • 定时任务:对于定时任务,可以使用timeout确保任务在规定时间内完成。

 

throttle

在某些情况下,我们可能希望限制事件的传递速率,以降低处理的频率。例如,在用户输入搜索关键字时,我们可能不希望每次输入都触发搜索请求,而是希望等待用户停止输入一段时间后再触发搜索。这时 throttle 操作符就派上用场了。

let publisher = PassthroughSubject()

// 使用throttle操作符
let cancellable = publisher
    .throttle(for: .seconds(6), scheduler: DispatchQueue.main, latest: true)
    .sink { value in
        print("Throttle: \(value)")
    }

// 发送元素
publisher.send(1)
publisher.send(2) 
publisher.send(3)

// 输出结果:
// Debounce: 3

 Parameters:

  • interval: 查找和发送最近的或第一个元素的时间间隔,用调度器的时间系统表示
  • scheduler: 发布元素的调度器 (publisher.send()调用的线程要和scheduler保持一致,且不能async, 否则会执行多遍)
  • latest: 布尔值,表示是否发布最新的元素。如果`false`, publisher会发出在间隔内接收到的第一个元素

常见用例

  • 搜索输入限制:在用户输入搜索关键字时,使用 throttle 以限制搜索请求的频率,提高性能。
  • UI界面更新:在某些情况下,限制 UI 界面更新的频率,以减少不必要的刷新。
  • 防抖处理:与搜索输入类似,防抖处理是一种限制触发频率的常见用例。

你可能感兴趣的:(swift)