Combine知识点switchToLatest

使用场景

  1. 搜索框自动补全(避免发送多个请求,只保留最后一个)
  2. 异步任务切换(如下载任务,视频播放切换,当用户连续点击了多个视频时,只播放最后一个,取消前几个的请求)
  3. 网络请求取消(当用户发起新的请求时,自动取消旧的请求)
  4. 用户交互事件 (点击按钮触发任务,只执行最新的点击任务)

switchToLatest解决的核心问题是:

  1. 自动取消旧的Publisher,只保留最新的Publisher
  2. 适用于"每次订阅都会创建新的Publisher"的情况

switchToLatest的定义

swift 复制代码
func switchToLatest()-> Publishers.SwitchToLatest<Upstream.Output, Upstream>

这里的Upstream指的是Publisher内部产生的Publisher,switchToLatest会从中只取最新的Publisher。

核心特点

  1. 适合于Publisher<Publisher<Output, Failure>, Failure>,即一个Publisher产生多个Publisher的情况
  2. 当新的Publiser出现时,会自动取消之前的Publisher,只执行最新的publisher。
  3. 只会订阅最新的Publiser并将其结果发送给下游

例1,防止重复的网络请求

场景

用户在搜索框中输入关键词时,每次输入都会触发新的API请求,我门希望只进行最新的一次请求,之前的取消。

swift 复制代码
mport Combine
import Foundation
// 模拟搜索 API
func searchAPI(query: String) -> AnyPublisher<String, Never> {
    Just("搜索结果: \(query)")
        .delay(for: .seconds(1), scheduler: DispatchQueue.global())  // 模拟网络请求延迟
        .eraseToAnyPublisher()
}

// 用户输入的搜索关键词
let searchText = PassthroughSubject<String, Never>()
// 处理搜索请求
let cancellable = searchText
    .map { query in searchAPI(query: query) }  // 每次输入都会生成一个新的 Publisher
    .switchToLatest()  // 只保留最新的 Publisher,取消旧的请求
    .sink { result in
        print(result)  // 只会输出最新搜索的结果
    }
// 模拟用户输入
searchText.send("Swift")    // 请求 1
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
    searchText.send("SwiftUI")  // 请求 2,取消 "Swift"
}
DispatchQueue.global().asyncAfter(deadline: .now() + 1.5) {
    searchText.send("Combine")  // 请求 3,取消 "SwiftUI"
}

// 输出结果
"搜索结果: Combine"

代码解析

  1. searchText是一个PassthroughSubject,用于模拟用户输入的关键词
  2. map { query in searchAPI(query: query)} 每次输入都会调用searchAPI生成新的Publisher(异步网络请求)
  3. swiftToLatest() 只保留最新的Publisher,如果新的请求到来,会取消旧的请求
  4. slink订阅最终的结果,只会收到最新的搜索返回的值
例2, 点击按钮触发异步任务
场景:

用户点击下载按钮时,每次点击都睡触发新的下载任务,我们希望只有最后一次的任务执行,之前的任务自动取消。

swift 复制代码
import Combine
import Foundation  

// 模拟下载任务
func downloadTask(_ id: Int) -> AnyPublisher<String, Never> {
    Just("任务 \(id) 下载完成")
        .delay(for: .seconds(2), scheduler: DispatchQueue.global())  // 模拟下载延迟
        .eraseToAnyPublisher()
}
// 用户点击按钮的触发器
let downloadButtonTap = PassthroughSubject<Int, Never>()
// 处理下载任务
let cancellable = downloadButtonTap
    .map { id in downloadTask(id) }  // 每次点击都会生成新的下载任务
    .switchToLatest()  // 只执行最新的任务,旧任务会被取消
    .sink { result in
        print(result)  // 只会输出最新任务的完成结果
    }
// 模拟用户点击
downloadButtonTap.send(1)  // 任务 1
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
    downloadButtonTap.send(2)  // 任务 2,取消任务 1
}
DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
    downloadButtonTap.send(3)  // 任务 3,取消任务 2
}

// 输出
// 任务3 下载完成

任务1 和 2 在完成之前被取消,所以他们的slink不会接收到任何输出

相关推荐
天天扭码26 分钟前
总所周知,JavaScript中有很多函数定义方式,如何“因地制宜”?(ˉ﹃ˉ)
前端·javascript·面试
一个专注写代码的程序媛30 分钟前
为什么vue的key值,不用index?
前端·javascript·vue.js
장숙혜42 分钟前
ElementUi的Dropdown下拉菜单的详细介绍及使用
前端·javascript·vue.js
火柴盒zhang44 分钟前
websheet之 编辑器
开发语言·前端·javascript·编辑器·spreadsheet·websheet
某公司摸鱼前端1 小时前
uniapp 仿企微左边公司切换页
前端·uni-app·企业微信
WKK_1 小时前
uniapp自定义封装tabbar
前端·javascript·小程序·uni-app
莫问alicia1 小时前
react 常用钩子 hooks 总结
前端·javascript·react.js
Mintopia1 小时前
图形学中的数学基础与 JavaScript 实践
前端·javascript·计算机图形学
Mintopia1 小时前
Three.js 制作飘摇的草:从基础到进阶的全流程教学
前端·javascript·three.js
BillKu1 小时前
Vue3父子组件数据双向同步实现方法
前端·javascript·vue.js