首页 > 其他分享 >Swift Combine — Debounce和Throttle的理解与使用

Swift Combine — Debounce和Throttle的理解与使用

时间:2024-06-24 11:29:34浏览次数:24  
标签:Throttle Debounce viewModel 0.5 发送 Combine sendMessage DispatchQueue main

DebounceThrottle 是两种常用的操作符,用于控制数据流的频率和处理延迟。但它们的实现方式略有不同。理解这些差异对于在Combine代码中做出正确选择至关重要。

Debounce

Debounce 操作符用于限制数据流的频率,只有在指定的时间间隔内没有新数据到达时,才会将最后一个数据发送出去。

public func debounce<S>(for dueTime: S.SchedulerTimeType.Stride, scheduler: S, options: S.SchedulerOptions? = nil) -> Publishers.Debounce<Self, S> where S : Scheduler
  1. for: 这是一个表示时间间隔的参数,指定了在没有新数据到达时等待的时间长度。可以是DispatchTimeInterval类型,如.seconds(1)表示1秒,.milliseconds(500)表示500毫秒等。
  2. scheduler: 这是一个调度器参数,用于指定在哪个调度器上执行等待和发送操作。通常可以使用DispatchQueue.main来在主队列上执行操作,也可以用RunLoop.main,也可以使用其他自定义的调度器。
  3. options: 这是一个可选的参数,用于指定额外的选项。基本不用,直接忽略。
class DebounceViewModel: ObservableObject {

  let publisher = PassthroughSubject<String, Never>()
  private var cancellable = Set<AnyCancellable>()
  @Published var outputArray: [String] = []

  init() {
    setUpSubscription()
  }

  func setUpSubscription() {
    publisher
      .debounce(for: .seconds(0.5), scheduler: RunLoop.main)
      .sink { [weak self] value in
        print("Received value: \(value)")
        self?.outputArray.append(value)
      }
      .store(in: &cancellable)
  }

  func sendMessage(_ text: String) {
    publisher.send(text)
  }
}

上面代码中,代码中添加了debounce方法,并设置了间隔时间为0.5秒。

执行原理

  • 当接收到新的值时,debounce启动一个定时器。
  • 如果在定时器到期前收到其他值,则复位定时器。
  • 在没有新的输入的情况下,计时器完成后才会发出最新的值。

SwiftUI界面中,模拟了用户连续输入的情况,比如下面代码中的viewModelSendMessage方法。

func viewModelSendMessage() {
    // 发送1,开始计时。
    viewModel.sendMessage("1")

    // 0.25秒后发送2,与上次发送间隔未超过0.5秒,停止上次计时,并且重新开始计时,记录最新值为2.
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
      viewModel.sendMessage("2")
    }

    // 0.5秒后发送3,与上次发送间隔未超过0.5秒,停止上次计时,并且重新开始计时,记录最新值为3.
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
      viewModel.sendMessage("3")
    }

    // 0.75秒后发送4,与上次发送间隔未超过0.5秒,停止上次计时,并且重新开始计时,记录最新值为4.
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
      viewModel.sendMessage("4")
    }

    // 1.3秒后发送5,与上次发送间隔超过0.5秒,所以4已经在1.25秒的时候发出去了,并订阅者收到。此时发送5并开始计时。
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.3) {
      viewModel.sendMessage("5")
    }
    // 2秒后发送6,与上次发送间隔超过0.5秒,所以5已经在1,8秒的时候发出去了,并订阅者收到。此时发送6并开始计时。发送6后没有再发送任何数据了,所以过0.5秒后,订阅者收到6.
    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
      viewModel.sendMessage("6")
    }
  }

代码注释中已经备注了执行原理。
在SwiftUI中,我们用一个List显示打印出来的结果,代码如下:

  @StateObject private var viewModel = DebounceViewModel()

  var body: some View {
    VStack {
      Button("Send messages") {
        viewModelSendMessage()
      }
      .buttonStyle(BorderedProminentButtonStyle())

      List(viewModel.outputArray, id: \.self) { value in
        Text(value)
          .font(.title)
          .frame(maxWidth: .infinity, alignment: .leading)
      }
      .listStyle(PlainListStyle())
    }
  }

执行效果如下:
在这里插入图片描述

使用场景

  • 当你想对用户输入或数据更改做出反应,但不想处理每个中间值时,Debounce特别有用。
  • 常见的用例包括搜索栏、文本输入字段或自动建议,您希望在开始搜索之前等待用户暂停输入。

Throttle

Throttle 操作符用于控制数据流的速率,只有在指定的时间间隔内才会发送数据,忽略掉间隔内的其他数据。

public func throttle<S>(for interval: S.SchedulerTimeType.Stride, scheduler: S, latest: Bool) -> Publishers.Throttle<Self, S> where S : Scheduler
  1. for: 这是一个表示时间间隔的参数,指定了每隔多长时间发送一次数据。可以是DispatchTimeInterval类型,如.seconds(1)表示1秒,.milliseconds(500)表示500毫秒等。
  2. scheduler: 这是一个调度器参数,用于指定在哪个调度器上执行等待和发送操作。通常可以使用DispatchQueue.main来在主队列上执行操作,也可以用RunLoop.main,也可以使用其他自定义的调度器。
  3. latest: 这是一个布尔值参数,用于指定是否只发送最新的数据。如果设置为true,则只发送最新的数据,忽略掉间隔内的其他数据;如果设置为false,则会发送间隔内的第一个数据。
class ThrottleDemoViewModel: ObservableObject {

  let publisher = PassthroughSubject<String, Never>()
  private var cancellable = Set<AnyCancellable>()
  @Published var outputArray: [String] = []

  init() {
    setUpSubscription()
  }

  func setUpSubscription() {
    publisher
      .throttle(for: 0.5, scheduler: DispatchQueue.main, latest: true)
      .sink { [weak self] value in
        print("Received value: \(value)")
        self?.outputArray.append(value)
      }
      .store(in: &cancellable)
  }

  func sendMessage(_ text: String) {
    publisher.send(text)
  }
}

上面代码中设置间隔时间为0.5秒,期间内发送最后一次的值。

执行原理

  • 当接收到新值时,启动计时器并允许该值通过。
  • 在计时器持续时间内收到的任何后续值都将被忽略。
  • 计时器到期后,该过程重复,允许下一个值通过并开始一个新的计时器。
struct ThrottleDemo: View {
  @StateObject private var viewModel = ThrottleDemoViewModel()

  var body: some View {
    VStack {
      Button("Send messages") {
        viewModelSendMessage()
      }
      .buttonStyle(BorderedProminentButtonStyle())

      List(viewModel.outputArray, id: \.self) { value in
        Text(value)
          .font(.title)
          .frame(maxWidth: .infinity, alignment: .leading)
      }
      .listStyle(PlainListStyle())
    }
  }

  func viewModelSendMessage() {
    // 1
    viewModel.sendMessage("1")
    // 2
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
      viewModel.sendMessage("2")
    }
    // 3
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
      viewModel.sendMessage("3")
    }
    // 4
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
      viewModel.sendMessage("4")
    }
    // 5
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
      viewModel.sendMessage("5")
    }
    // 6
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.3) {
      viewModel.sendMessage("6")
    }
  }
}

viewModelSendMessage方法中,发送1后,直接让1通过,并开始计时:
0~0.5秒内:发送了2和3。最终3通过(如果latest为false,2通过)。
0.5~1.0秒内:发送了4和5。最终5通过(如果latest为false,4通过)。
1.0~1.5秒内:发送了6。最终6通过。

执行效果如下(latest为true):
在这里插入图片描述
执行效果如下(latest为false):
在这里插入图片描述

使用场景

  • 当你想要强制一个一致的更新速度,或者当你想要防止超载的下游系统与过多的数据。
  • 它通常用于滚动事件或处理UI组件中的用户交互等场景(比如防止连续点击Button)。

写在最后

理解Combinedebouncethrottle的区别对于有效的事件处理和数据流控制至关重要。
Debounce 操作符用于限制数据流的频率,只有在指定的时间间隔内没有新数据到达时,才会将最后一个数据发送出去。
Throttle 操作符用于控制数据流的速率,只有在指定的时间间隔内才会发送数据,忽略掉间隔内的其他数据。

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

标签:Throttle,Debounce,viewModel,0.5,发送,Combine,sendMessage,DispatchQueue,main
From: https://blog.csdn.net/guoyongming925/article/details/139870110

相关文章

  • c#中path.combine的用法是什么
    原文链接:https://www.yisu.com/ask/29579392.html在C#中,Path.Combine()方法用于将两个或多个字符串路径组合成一个有效的路径。它接受多个字符串参数作为路径的组成部分,并返回一个字符串,表示有效的路径。语法如下:publicstaticstringCombine(paramsstring[]paths);参数pa......
  • uniapp中防抖函数debounce的使用
    uniapp中防抖函数debounce的使用分段控件u-subsection每次点击一个tab的时候都会ajax访问一次接口取列表数据的,这时如果有人快速在多个分段间快速点击的话,每次点击都会访问接口的,网上找了好多资料,终于找到了这个玩意。。。记得以前也弄过这个的。。不过当时没有记下来,现在记下来......
  • WPF Path GeometryCombineMode Union, Exclude,Intersect,Xor
    union<Windowx:Class="WpfApp172.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mi......
  • 防抖函数 debounce 和节流函数 throttle
    明天考蓝桥web,复习下debounce和throttledebounce防抖函数应用场景:即时响应式的输入框、按钮等(毕竟没人知道用户究竟会怎么用)前置知识:闭包:与很多主流编程语言不同,JavaScript在变量作用域上具有一些特殊表现。(摘自mdn)闭包是由函数以及声明该函数的词法环境组合而成的。该环......
  • Charles 弱网测试【Throttle Setting】
           不开弱网:      开启弱网:       开启弱网后: ......
  • Oracle Hint "index_combine"对于like的局限性
     OracleHint"index_combine"对于like的局限性 数据库版本:11.2.0.4.0今天遇到1条问题SQL,优化遇到一点问题,SQL文本大概如下:其中,col2和col3存在索引的且选择性都很高,col1的业务特性是只有两个值选择性低没有也不适合建Btree索引。导致每次执行都是全表扫描,统计发现这条SQL......
  • 深入理解 Swift Combine
    Combine文中写一些Swift方法签名时,会带上label,如subscribe(_subscriber:),正常作为Selector的写法时会忽略掉label,只写作subscribe(_:),本文特意带上label以使含义更清晰。CombineFrameworkOverview在App运行过程中会发生各种各样的异步事件,如网络请求的返回,No......
  • 30 天精通 RxJS (14):Observable Operator - throttle, debounce
    昨天讲到了在UI操作上很常用的delay,今天我们接着要来讲另外两个也非常实用operators,尤其在做性能优化时更是不可或缺的好工具!Operatorsdebounce跟buffer、bufferTime一样,Rx有debounce跟debounceTime一个是传入observable另一个则是传入毫秒,比较常用到的是de......
  • 详细解读JavaScript中的防抖(debounce)和节流(throttle)!!!
    在JavaScript中,防抖(debounce)和节流(throttle)是两种常用的技术,用于限制函数的执行频率,特别是在处理高频事件(如窗口的resize、scroll,输入框的keyup、mousedown等)时非常有用。防抖(debounce)防抖的基本思想是将多次执行变为最后一次执行。也就是说,在事件被触发后n秒内函数只能执......
  • A visual method to detect meat adulteration by recombinase polymerase amplifica
    创新点:基于重组酶聚合酶扩增(RPA)和侧向流试纸(LFD)的视觉方法,用于鉴定牛肉(Bostaurus)、绵羊(Ovisaries)、猪肉(Susscrofa)、鸭肉(Anasplatyrhynchos)和鸡肉(Gallusgallus)的动物来源。传统的方法需要操作员具备相当的技能、昂贵的仪器,并且无法提供快速的移动式现场检测系统来检测肉制品的......