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

news2024/12/23 5:34:25

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 操作符用于控制数据流的速率,只有在指定的时间间隔内才会发送数据,忽略掉间隔内的其他数据。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1860256.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目管理的六个核心内容

项目管理是一个系统性和综合性的过程&#xff0c;涉及多个核心内容的协同管理&#xff0c;以确保项目能够按时、按预算、高质量的完成&#xff0c;以下是项目管理的六个核心内容&#xff1a; 一、项目目标与范围 项目目标与范围是项目管理的起点和基础&#xff0c;在项目启动…

【linux网络(六)】IP协议详解

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux网络 1. 前言2. IP协议报…

AI大模型企业应用实战(23)-Langchain中的Agents如何实现?

0 前言 这将所有可用的代理按照几个维度进行分类。 预期模型类型 用于聊天模型&#xff08;接收信息&#xff0c;输出信息&#xff09;或 LLM&#xff08;接收字符串&#xff0c;输出字符串&#xff09;。这主要影响所使用的提示策略。 支持聊天历史记录 这些代理类型是否…

Cesium--旋转3dtiles

以下代码来自Cesium 论坛&#xff1a;3DTileset rotation - CesiumJS - Cesium Community 在1.118中测试可行&#xff0c;可直接在Sandcastle中运行&#xff1a; const viewer new Cesium.Viewer("cesiumContainer", {terrain: Cesium.Terrain.fromWorldTerrain()…

零基础小白应该如何安装Python?(包含Windows、MacOS、Linux)

1. 安装前的准备工作 在安装Python之前&#xff0c;我们需要了解以下几个问题&#xff1a; 确保计算机连接到互联网确认操作系统版本&#xff08;Windows、MacOS、Linux&#xff09;决定安装Python的版本&#xff08;建议安装最新的稳定版本&#xff09; 2. 在Windows上安装…

docker技术的说明

根据学习网站整理&#xff1a;Docker 10分钟快速入门_哔哩哔哩_bilibili 小白也能看懂的容器科普说明_哔哩哔哩_bilibili 1.虚拟机&#xff0c;需要模拟硬件系统、运行整个操作系统&#xff0c;但体积臃肿&#xff0c;内存占用较高&#xff0c;程序的性能也会受到影响。 2.…

无中心化崛起:Web3对传统互联网的冲击与重构

随着Web3技术的兴起&#xff0c;传统互联网面临着前所未有的挑战和重构。本文将深入探讨Web3的无中心化特性如何对传统互联网产生冲击&#xff0c;以及其可能带来的重大影响和未来发展趋势。 1. 传统互联网的局限与问题 传统互联网&#xff0c;通常称为Web2&#xff0c;主要依…

Go 如何使用指针灵活操作内存

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Vue3整合Tailwindcss实现渲染动态类

创建项目 pnpm create vite整合Tailwindcss 安装依赖&#xff1a; pnpm install -D tailwindcss postcss autoprefixer生成配置文件&#xff1a; npx tailwindcss initpostcss.config.js export default {plugins: {tailwindcss: {},autoprefixer: {},}}tailwind.config.j…

【产品经理】订单处理10-分配快递策略

本次主要讲解下在订单处理过程中分配快递的策略以及分配快递中需要用到的设置。 一、建立快递档案 在ERP系统中&#xff0c;需要建立快递档案&#xff0c;设置所属快递、快递的服务类型、支持的打印模版以及快递在各个平台的电子面单支持情况。 二、仓库绑定快递 仓库需要设…

golang 实现继承方式

经常使用java或c同学应该比较了解纯面向对象&#xff0c;继承、接口、封装等特性&#xff0c;在go中并没有特别显示的表达出来&#xff0c;但是go隐含是支持的&#xff0c;只是支持的方式不一致&#xff0c;可以说go的方式更加灵活&#xff0c;go语言精髓是组合机制&#xff0c…

一口气安装【Python】教程

浏览器搜索python&#xff0c;或者直接跳转网址。 https://www.python.orghttps://www.python.org/ 找到想下载的版本 根据自己电脑下载相应的版本 自定义安装 下一步 修改路径&#xff0c;然后点击安装 等待一会&#xff0c;喝个饮料 点击关闭 安装成功 安装结束

CEWEY C9自动猫砂盆测评:千元级安全实用稳定输出,解放铲屎官双手!

最近邻居姐姐成为新晋铲屎官&#xff0c;猫咪的吃喝还好&#xff0c;因为是打工人每天要早出晚归&#xff0c;铲屎这项不能等待的任务就让她很苦恼&#xff0c;猫砂盆太脏猫咪要么憋着不上要么乱拉乱尿&#xff0c;搞得小姐姐身心俱疲。看着她日渐憔悴的脸色&#xff0c;我这个…

软考中级复习过程

中级软考复习过程 先上成绩截图 ~~~~ 总结一下自己的软考中级备考过程&#xff0c;个人备考的是软件设计师&#xff0c;首先对于软考中的大部分内容其实我都学过的&#xff0c;只是有些内容确实会忘记&#xff0c;我把整个备考的过程分为前后两个阶段。 ~~~~ 前期阶段&#…

复分析——第7章——ζ 函数和素数定理(E.M. Stein R. Shakarchi)

第7章 ζ函数和素数定理 Bernhard Riemann, whose extraordinary intuitive powers we have already mentioned, has especially renovated our knowledge of the distribution of prime numbers, also one of the most mysterious questions in mathematics. He has tau…

如何在 Odoo 17 库存模块中进行质量控制

质量控制是确保制造产品质量符合最终要求的关键步骤。有效的质量控制能够提高客户满意度。在生产过程中,质量检测可以在多个环节进行,以便及时发现并解决问题。不同的行业采用的质量控制技术可能有所不同。 在商业尤其是制造业中,保证产品质量至关重要。一个产品需要经过多次…

百度地图使用任意图片旋转任意角度作为地面贴图

公司项目有个需求是要在地图上贴个航拍的照片做出类似卫星地图的效果&#xff0c;但是只有一张图片而且可以随时替换&#xff0c;也不好做瓦片地图&#xff0c;而且照片的角度可以任意旋转。 要实现这个功能需要解决以下问题&#xff1a; 百度地图怎么贴图片图片角度如何旋转 …

el-dialog使用::v-deep()穿透设置样式不生效,解决办法亲测有效!

场景&#xff1a; <el-dialogv-model"dialogVisible"width"800px":before-close"beforeClose"append-to-body:close-on-click-modal"false"title"增加文档"><template #footer><div style"text-align:c…

开发大模型应用,到底使用RAG还是微调?我们应该从哪些方面考虑?

现在基于大模型开发应用时&#xff0c;相信很多人都有这种疑问&#xff0c;到底对大模型进行微调还是外接RAG呢&#xff1f;因为两者在一定层面上有很多相似的地方&#xff0c;下面让我给大家从各个层面进行分析&#xff0c;结合具体的业务场景&#xff0c;看哪种方式更适合你的…

【OnlyOffice】 桌面应用编辑器,版本8.1发布,PDF编辑器、幻灯片版式、改进从右至左显示、新的本地化选项等功能,快来体验吧

继 ONLYOFFICE 文档 8.1 发布后&#xff0c;适用于 Linux、Windows 和 macOS 的 ONLYOFFICE 桌面应用程序最新版本也已推出。它具有在线套件的最主要功能&#xff0c;例如功能齐全的 PDF 编辑器、演示文稿中的幻灯片版式、改进的 RTL 支持、新的本地化选项等。 目录 ONLYOFFICE…