Swift Combine 有序的异步操作 从入门到精通十二
Combine 系列
- Swift Combine 从入门到精通一
- Swift Combine 发布者订阅者操作者 从入门到精通二
- Swift Combine 管道 从入门到精通三
- Swift Combine 发布者publisher的生命周期 从入门到精通四
- Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五
- Swift Combine 订阅者Subscriber的生命周期 从入门到精通六
- Swift 使用 Combine 进行开发 从入门到精通七
- Swift 使用 Combine 管道和线程进行开发 从入门到精通八
- Swift Combine 使用 sink, assign 创建一个订阅者 从入门到精通九
- Swift Combine 使用 dataTaskPublisher 发起网络请求 从入门到精通十
- Swift Combine 用 Future 来封装异步请求 从入门到精通十一

目的:使用 Combine 的管道来显式地对异步操作进行排序
这类似于一个叫做 “promise chaining” 的概念。 虽然你可以将 Combine 处理的和其行为一致,但它可能不能良好地替代对 promise 库的使用。 主要区别在于,promise 库总是将每个 promise 作为单一结果处理,而 Combine 带来了可能需要处理许多值的复杂性。
任何需要按特定顺序执行的异步(或同步)任务组都可以使用 Combine 管道进行协调管理。 通过使用 Future 操作符,可以捕获完成异步请求的行为,序列操作符提供了这种协调功能的结构。
通过将任何异步 API 请求与 Future 发布者进行封装,然后将其与 flatMap 操作符链接在一起,你可以以特定顺序调用被封装的异步 API 请求。 通过使用 Future 或其他发布者创建多个管道,使用 zip 操作符将它们合并之后等待管道完成,通过这种方法可以创建多个并行的异步请求。
如果你想强制一个 Future 发布者直到另一个发布者完成之后才被调用,你可以把 future 发布者创建在 flatMap 的闭包中,这样它就会等待有值被传入 flatMap 操作符之后才会被创建。
通过组合这些技术,可以创建任何并行或串行任务的结构。
如果后面的任务需要较早任务的数据,这种协调异步请求的技术会特别有效。 在这些情况下,所需的数据结果可以直接通过管道传输。
此排序的示例如下。 在此示例中,按钮在完成时会高亮显示,按钮的排列顺序是特意用来显示操作顺序的。 整个序列由单独的按钮操作触发,该操作还会重置所有按钮的状态,如果序列中有尚未完成的任务,则都将被取消。 在此示例中,异步 API 请求会在随机的时间之后完成,作为例子来展示时序的工作原理。
创建的工作流分步表示如下:
- 步骤 1 先运行。
- 步骤 2 有三个并行的任务,在步骤 1 完成之后运行。
- 步骤 3 等步骤 2 的三个任务全部完成之后,再开始执行。
- 步骤 4 在步骤 3 完成之后开始执行。
此外,还有一个 activity indicator 被触发,以便在序列开始时开始动画,在第 4 步完成时停止。
UIKit-Combine/AsyncCoordinatorViewController.swift
import UIKit
import Combineclass AsyncCoordinatorViewController: UIViewController {@IBOutlet weak var startButton: UIButton!@IBOutlet weak var step1_button: UIButton!@IBOutlet weak var step2_1_button: UIButton!@IBOutlet weak var step2_2_button: UIButton!@IBOutlet weak var step2_3_button: UIButton!@IBOutlet weak var step3_button: UIButton!@IBOutlet weak var step4_button: UIButton!@IBOutlet weak var activityIndicator: UIActivityIndicatorView!var cancellable: AnyCancellable?var coordinatedPipeline: AnyPublisher<Bool, Error>?@IBAction func doit(_ sender: Any) {runItAll()}func runItAll() {if self.cancellable != nil { // 1print("Cancelling existing run")cancellable?.cancel()self.activityIndicator.stopAnimating()}print("resetting all the steps")self.resetAllSteps() // 2// driving it by attaching it to .sinkself.activityIndicator.startAnimating() // 3print("attaching a new sink to start things going")self.cancellable = coordinatedPipeline? // 4.print().sink(receiveCompletion: { completion inprint(".sink() received the completion: ", String(describing: completion))self.activityIndicator.stopAnimating()}, receiveValue: { value inprint(".sink() received value: ", value)})}// MARK: - helper pieces that would normally be in other files// this emulates an async API call with a completion callback// it does nothing other than wait and ultimately return with a boolean valuefunc randomAsyncAPI(completion completionBlock: @escaping ((Bool, Error?) -> Void)) {DispatchQueue.global(qos: .background).async {sleep(.random(in: 1...4))completionBlock(true, nil)}}/// Creates and returns pipeline that uses a Future to wrap randomAsyncAPI/// and then updates a UIButton to represent the completion of the async/// work before returning a boolean True./// - Parameter button: button to be updatedfunc createFuturePublisher(button: UIButton) -> AnyPublisher<Bool, Error> { // 5return Future<Bool, Error> { promise inself.randomAsyncAPI() { (result, err) inif let err = err {promise(.failure(err))} else {promise(.success(result))}}}.receive(on: RunLoop.main)// so that we can update UI elements to show the "completion"// of this step.map { inValue -> Bool in // 6// intentionally side effecting here to show progress of pipelineself.markStepDone(button: button)return true}.eraseToAnyPublisher()}/// highlights a button and changes the background color to green/// - Parameter button: reference to button being updatedfunc markStepDone(button: UIButton) {button.backgroundColor = .systemGreenbutton.isHighlighted = true}func resetAllSteps() {for button in [self.step1_button, self.step2_1_button, self.step2_2_button, self.step2_3_button, self.step3_button, self.step4_button] {button?.backgroundColor = .lightGraybutton?.isHighlighted = false}self.activityIndicator.stopAnimating()}// MARK: - view setupoverride func viewDidLoad() {super.viewDidLoad()self.activityIndicator.stopAnimating()// Do any additional setup after loading the view.coordinatedPipeline = createFuturePublisher(button: self.step1_button) // 7.flatMap { flatMapInValue -> AnyPublisher<Bool, Error> inlet step2_1 = self.createFuturePublisher(button: self.step2_1_button)let step2_2 = self.createFuturePublisher(button: self.step2_2_button)let step2_3 = self.createFuturePublisher(button: self.step2_3_button)return Publishers.Zip3(step2_1, step2_2, step2_3).map { _ -> Bool inreturn true}.eraseToAnyPublisher()}.flatMap { _ inreturn self.createFuturePublisher(button: self.step3_button)}.flatMap { _ inreturn self.createFuturePublisher(button: self.step4_button)}.eraseToAnyPublisher()}
}
runItAll协调此工作流的进行,它从检查当前是否正在执行开始。 如果是,它会在当前的订阅者上调用cancel()。resetAllSteps通过遍历所有表示当前工作流状态的按钮,并将它们重置为灰色和未高亮以回到初始状态。 它还验证 activity indicator 当前未处于动画中。- 然后我们开始执行请求,首先开启 activity indicator 的旋转动画。
- 使用
sink创建订阅者并存储对工作流的引用。 被订阅的发布者是在该函数外创建的,允许被多次复用。 管道中的print操作符用于调试,在触发管道时在控制台显示输出。 - 每个步骤都由
Future发布者紧跟管道构建而成,然后立即由管道操作符切换到主线程,然后更新 UIButton 的背景色,以显示该步骤已完成。 这封装在createFuturePublisher的调用中,使用 eraseToAnyPublisher 以简化返回的类型。 map操作符用于创建并更新UIButton,作为特定的效果以显示步骤已完成。- 创建整个管道及其串行和并行任务结构,是结合了对
createFuturePublisher的调用以及对flatMap和zip操作符的使用共同完成的。
另请参阅
- 通过包装基于 delegate 的 API 创建重复发布者
- 使用此代码的 ViewController 在 github 的项目中 UIKit-Combine/AsyncCoordinatorViewController.swift.
参考
https://heckj.github.io/swiftui-notes/index_zh-CN.html
代码
https://github.com/heckj/swiftui-notes
相关文章:
Swift Combine 有序的异步操作 从入门到精通十二
Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二Swift Combine 管道 从入门到精通三Swift Combine 发布者publisher的生命周期 从入门到精通四Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五Swift Com…...
国产航顺HK32F030M: 超声波测距模块串口通信数据接收与处理
参考代码 /************************************************************************************************** * file usart_async_tx_no_int_rx_rxneint.c * brief 异步串口通信例程, 通过查询TXE标志发送数据,通过RXNE中断接收数据,当中断接收到数据后会将 * …...
idea:如何连接数据库
1、在idea中打开database: 2、点击 ‘’ ---> Data Source ---> MySQL 3、输入自己的账号和密码其他空白处可以不填,用户和密码可以在自己的mysql数据库中查看 4、最后选择自己需要用的数据库,点击运用ok,等待刷新即可 最后:…...
JS中ES5和ES6的区别
前言 ES5是JavaScript的第五个修订版本,于2009年发布。而ES6是JavaScript的第六个修订版本,也称为ES2015,于2015年发布。以下是它们两个版本之前的一些区别: 变量声明方式 在ES5中,使用var关键字进行变量声明&#…...
软考24-上午题-图1
一、数据结构的回忆 线性结构:(一对一) 除首结点没有前驱、末尾结点没有后继外,一个结点只有唯一的一个直接前驱和唯一的一个直接后继。 树结构:(一对多) 除根节点没有前驱节点外,…...
书生·浦语大模型第四课作业
基础作业: 构建数据集,使用 XTuner 微调 InternLM-Chat-7B 模型, 让模型学习到它是你的智能小助手,效果如下图所示,本作业训练出来的模型的输出需要将不要葱姜蒜大佬替换成自己名字或昵称! 1.安装 # 如果你是在 Int…...
勒索攻击风起云涌,Sodinokibi深度分析
前言 Sodinokibi勒索病毒,又称为REvil勒索病毒,这款勒索病毒最早在国内被发现是2019年4月份,笔者在早期分析这款勒索病毒的时候就发现它与其他勒索病毒不同,于是被笔者称为GandCrab勒索病毒的“接班人”,为什么它是Ga…...
1124. 骑马修栅栏(欧拉路径,模板)
农民John每年有很多栅栏要修理。 他总是骑着马穿过每一个栅栏并修复它破损的地方。 John是一个与其他农民一样懒的人。 他讨厌骑马,因此从来不两次经过一个栅栏。 你必须编一个程序,读入栅栏网络的描述,并计算出一条修栅栏的路径…...
C# CAD2016获取数据操作BlockTableRecord、Polyline、DBObject
一、数据操作说明 //DBObject 基础类 DBObject dbObj (DBObject)tr.GetObject(outerId, OpenMode.ForRead); //Polyline 线段类 Polyline outerPolyline (Polyline)tr.GetObject(outerId, OpenMode.ForRead); //BlockTableRecord 块表类 BlockTableRecord modelSpace (Bloc…...
java SSM新闻管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计
一、源码特点 java SSM新闻管理系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S…...
Linux_线程
线程与进程 多级页表 线程控制 线程互斥 线程同步 生产者消费者模型 常见概念 下面选取32位系统举例。 一.线程与进程 上图是曾经我们认为进程所占用的资源的集合。 1.1 线程概念 线程是一个执行分支,执行粒度比进程细,调度成本比进程低线程是cpu…...
【selenium】
selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的。Selenium可以直接调用浏览器,它支持所有主流的浏览器。其本质是通过驱动浏览器,完成模拟浏览器操作,比如挑战,输入,点击等。 下载与打…...
HX711压力传感器学习一(STM32)
目录 原理图: 引脚介绍: HX711介绍工作原理: 程序讲解: 整套工程: 发送的代码工程,与博客的不一致,如果编译有报错请按照报错和博客进行修改 原理图: 引脚介绍: VCC和GND引…...
作业2.13
1、选择题 1.1、若有定义语句:int a[3][6]; ,按在内存中的存放顺序,a 数组的第10个元素是 D A)a[0][4] B) a[1][3] C)a[0][3] D)a[1][4] 1.2、有数组 int a[5] {10,20,30,40,50},…...
ArcGIS学习(七)图片数据矢量化
ArcGIS学习(七)图片数据矢量化 通过上面几个任务的学习,大家应该已经掌握了ArcGIS的基础操作,并且学习了坐标系和地理数据库这两个非常重要且稍微难一些的专题。从这一任务开始,让我们进入到实战案例板块。 首先进入第一个案例一一图片数据矢量化。 我们在平时的工作学…...
G口大流量服务器选择的关键点有哪些?
G口服务器指的是接入互联网的带宽达到1Gbps以上的服务器,那么选择使用G口大流量服务器的用户需要注意哪些选择 关键点呢?小编为您整理关于G口大流量服务器的关键点。 G口服务器通常被用于需要大带宽支持的业务场景,比如视频流媒体、金融交易平台、电子商…...
MongoDB聚合:$unset
使用$unset阶段可移除文档中的某些字段。从版本4.2开始支持。 语法 移除单个字段,可以直接指定要移除的字段名: { $unset: "<field>" }移除多个字段,可以指定一个要移除字段名的数组: { $unset: [ "<…...
DS Wannabe之5-AM Project: DS 30day int prep day14
Q1. What is Alexnet? Q2. What is VGGNet? Q3. What is VGG16? Q4. What is ResNet? At the ILSVRC 2015, so-called Residual Neural Network (ResNet) by the Kaiming He et al introduced the anovel architecture with “skip connections” and features heavy b…...
【程序设计竞赛】C++与Java的细节优化
必须强调下,以下的任意一种优化,都应该是在本身采用的算法没有任何问题情况下的“锦上添花”,而不是“雪中送炭”。 如果下面的说法存在误导,请专业大佬评论指正 读写优化 C读写优化——解除流绑定 在ACM里,经常出现…...
Java缓冲流——效率提升深度解析
前言 大家好,我是chowley,在我之前的项目中,用到了缓冲流来提高字符流之间的比较速度,缓冲流的主要作用类似于数据库缓存,提高IO操作效率。 缓冲流 在Java的输入输出操作中,缓冲流是提高性能的重要工具之…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...
Windows 下端口占用排查与释放全攻略
Windows 下端口占用排查与释放全攻略 在开发和运维过程中,经常会遇到端口被占用的问题(如 8080、3306 等常用端口)。本文将详细介绍如何通过命令行和图形化界面快速定位并释放被占用的端口,帮助你高效解决此类问题。 一、准…...
