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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Information security in DLMS/COSEM(Green-Book)—认证机制

Information security in DLMS/COSEM 9.2.1 概述9.2.2 DLMS/COSEM安全概念9.2.2.1 概述 9.2.2.1 概述9.2.2.2 身份识别和认证9.2.2.2.1 身份识别9.2.2.2.2 认证机制9.2.2.2.2.1 概述 无安全认证&#xff08;Lowest Level Security&#xff09;&#xff1a;低级别安全认证&#…

Vue3 + Element-plus + TS —— 动态表格自由编辑

前期回顾 《 穿越时空的代码、在回首&#xff1a;Evil.js两年后的全新解读 》-CSDN博客 Vue3 TS Element-Plus 封装Tree组件 《亲测可用》_ https://blog.csdn.net/m0_57904695/article/details/131664157?spm1001.2014.3001.5501 态表格 自由编辑 目录 ♻️ 效果图…

IS022000与HACCP:提升食品安全管理的完美结合

国际标准化组织&#xff08;ISO&#xff09;于2005年9月发布了IS022000:2005标准&#xff0c;这是一项针对食品安全管理体系的国际标准。我国以等同采用的方式制定了国家标准GB/T 22000-2006《食品安全管理体系食品链中各类组织的要求》&#xff08;以下简称“GB/T22000”&…

Docker搭建yolov8并训练、验证、推理化学仪器数据集

目录 1、安装docker 2、创建yolov8镜像 3、下载代码包 4、下载模型预训练权重 5、制作数据集 6、训练、验证及推理 &#xff08;1&#xff09;训练 &#xff08;2&#xff09;验证 &#xff08;3&#xff09;推理 中文标签显示问题 本文通过docker的方式搭建yolov8运…

C语言入门课程学习笔记10:结构体联合体位域

C语言入门课程学习笔记10 第48课 - 自定义数据类型&#xff08;上&#xff09;实验-typedef实验小结 第49课 - 自定义数据类型&#xff08;中&#xff09;实验实验小结 第50课 - 自定义数据类型&#xff08;下&#xff09;实验实验小结 第51课 - 多文件程序设计实验实验实验小结…

python项目加密和增加时间许可证

1.bat&#xff0c;执行如下的命令&#xff0c;第一句是更新或增加许可证 第二句是加密draw_face.py python offer.py pyarmor obfuscate -O dist draw_face.py绘制自制人脸.py&#xff0c;调用加密的代码draw_face代码 import sys import os import cv2# 添加加密模块所在的路…

[MYSQL] 数据库基础

1.什么是数据库 从数据库的名字可以看出,它是用来操作(增删查改....)数据的,事实上也的确如此,通过数据库,我们可以更方便.更高效的来操作.管理数据 以文件形式存储数据的缺点 文件的安全问题文件不利于数据的查询和删除文件不利于存储海量数据操作文件并不方便 为了解决上述问…

煤矿运输新篇章:数字孪生模型引领智能化转型

在科技日新月异的今天&#xff0c;煤矿行业也迎来了前所未有的发展机遇。在这个充满挑战与机遇的时代&#xff0c;煤矿运输数字孪生模型以其独特的魅力和巨大的潜力&#xff0c;引领着煤矿运输领域走向智能化、高效化的新时代。 数字孪生模型&#xff0c;就是在虚拟世界中构建一…

喜讯:ISO年度审核通过!

在数字化时代&#xff0c;质量是我们不变的追求。近日&#xff0c;矩阵起源迎来了一个值得庆祝的时刻——三项ISO体系年度考核顺利通过&#xff01;分别为&#xff1a;ISO9001 质量管理体系标准认证、ISO20000信息技术服务管理体系认证及ISO27001 信息安全管理体系认证。 ISO标…

【分布式事务】分布式事务理论

CAP 理论 一致性&#xff08;Consistency&#xff09; 分布式系统中所有数据备份&#xff0c;在同一时刻是否是同样的值 可用性&#xff08;Availability&#xff09; 集群中一部分节点故障后&#xff0c;集群整体是否还能响应客户端的读写请求 分区容错性&#xff08;Partit…

移动硬盘损坏无法读取:故障解析与数据恢复策略

一、现象描述&#xff1a;移动硬盘损坏无法读取的困境 在数字化时代&#xff0c;移动硬盘作为数据存储的重要工具&#xff0c;广泛应用于个人和企业中。然而&#xff0c;当移动硬盘突然损坏&#xff0c;无法被系统正常读取时&#xff0c;往往会带来极大的困扰。用户可能会遇到…

《2024天猫618大促-首波男装销售报告》

这份报告主要分析了2024年天猫618大促期间的首波男装销售情况,从多个维度进行了深入的复盘和分析。报告中不仅包含了销售数据的统计分析,还对消费者行为、品牌表现、产品趋势等方面进行了详细的解读。通过对这些数据和信息的深入挖掘,报告揭示了当前男装市场的一些重要趋势和特…

冻干食品市场飙升至新高度,预计到 2030 年将达到 717 亿美元

冻干食品市场&#xff0c;近年来经历了显著增长&#xff0c;2021 年价值 372 亿美元&#xff0c;预计到 2030 年将达到 717 亿美元。 从2022年到2030年&#xff0c;这一强劲的扩张是由7.7%的复合年增长率推动的&#xff0c;这是由于多种因素造成的&#xff0c;包括食品加工行…

Linux命令重温

目录 Linux安装基础命令lsllcdpwdmkdirrmdirtouchcpmvrmvi/vim>和>>catheadlessmoretailechoclearwhich 进阶命令其他命令 Linux安装 通过vmware设置模拟硬件环境安装centos系统进行相应的网络配置安装xshell bin 存放二进制可执行文件(ls,cat,mkdir等) boot 存放用…

【STM32+FPGA】先进算力+强安全+边缘AI,64位STM32MP2聚焦工业4.0应用

工业应用数字化和智能化程度&#xff0c;是衡量新质生产力的重要标准。STM32最新一代64位微处理器STM32MP2凭借先进算力、丰富接口和高安全性&#xff0c;为高性能和高度互联的工业4.0应用赋能。 STM32MP2四大关键特性&#xff0c;为工业4.0应用赋能 STM32MP2系列的第一颗产品S…

Java项目:基于SSM框架实现的电子竞技管理平台【ssm+B/S架构+源码+数据库+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的电子竞技管理平台 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功能…

【Python时序预测系列】基于LSTM实现单变量时序序列多步预测(案例+源码)

这是我的第307篇原创文章。 一、引言 单站点单变量输入单变量输出多步预测问题----基于LSTM实现。 单输入就是输入1个特征变量 单输出就是预测出1个标签的结果 多步就是利用过去N天预测未来M天的结果 二、实现过程 2.1 读取数据集 # 读取数据集 data pd.read_csv(data.c…

超级内卷时代,这样做,刻不容缓

分享一个月入十万的赚钱项目 别再抱怨手上项目不好做&#xff0c;生意不好做了&#xff0c;这只是个开始&#xff0c;长远来看&#xff0c;2024应该是未来几年中经济环境最好的一年&#xff0c;我们所有人都已经进入到“超级内卷”时代&#xff01; 随着竞争环境越来越激烈&…

欢乐钓鱼大师游戏攻略:内置免费辅助工具的云手机!自动钓鱼!

《欢乐钓鱼大师》是一款极具趣味性和挑战性的钓鱼模拟游戏&#xff0c;玩家可以在虚拟的世界中体验到真实钓鱼的乐趣。本文将详细介绍游戏的各个方面&#xff0c;包括基本操作、鱼种识别、装备选择、技巧提升等&#xff0c;帮助玩家快速上手并逐步提升钓鱼技能。 《欢乐钓鱼大师…

010-基于Sklearn的机器学习入门:聚类(上)

本节及后续章节将介绍深度学习中的几种聚类算法&#xff0c;所选方法都在Sklearn库中聚类模块有具体实现。本节为上篇&#xff0c;将介绍几种相对基础的聚类算法&#xff0c;包括K-均值算法和均值漂移算法。 目录 10.1 聚类概述 10.1.1 聚类的种类 10.1.2 Sklearn聚类子模…