本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 —— 4.在 Combine 中执行异步代码

内容概览

  • 前言
  • Future 取代回调闭包
  • 用输出类型(Output Types)代表 Future 的参数
  • Subject 取代重复执行的闭包
  • 总结

前言

你的应用可能会使用一些常见的模式来处理异步事件,比如:

  • 完成处理器(Completion handlers)。它其实是调用方提供的一个闭包,当一个耗时任务完成后,这个闭包会被调用一次;
  • 闭包属性(Closure properties)。它其实也是调用方提供的一个闭包,这个闭包会在每一次异步事件发生时被调用;

Combine 为这些模式提供了强大的替换选项。它可以让你消除这种样板代码,并且充分利用 Combine 中的操作符。当你在应用的其他地方采用 Combine 时,将异步调用点转换为 Combine 可以提高代码的一致性和可读性。

Future 取代回调闭包

一个完成处理器其实就是一个传给某个方法的闭包参数,当这个方法完成任务时,它就会调用这个闭包。

比如下面的代码,这个函数接收一个闭包,然后在延时2秒之后执行这个闭包:

func performAsyncAction(completionHandler: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
completionHandler()
}
}

你可以用 Combine 中的 Future 去替换这种模式的代码。用 Future 创建一个发布者去执行一些工作,然后异步发送成功或者失败的信号。如果执行成功,Future 就会执行一个 Future.Promise,这是一个用来接收 Future 生成的元素的闭包。

你可以像这样重写上面的代码:

func performAsyncActionAsFuture() -> Future <Void, Never> {
return Future() { promise in
DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
promise(Result.success(()))
}
}
}

当任务完成时,Future 会调用传入的 promise, 并把代表执行成功或者失败的结果 Result 传给 promise,而不是像之前的代码那样调用一个显式传入的闭包。然后,调用方会通过 Future 异步接收这个结果。由于 Future 是一个 Combine 发布者,调用方会把自己连接到一个可选的操作符链上,链的尾部是一个订阅者,比如:sink(receiveValue:)

调用方的代码如下所示:

cancellable = performAsyncActionAsFuture()
.sink() { _ in print("Future succeeded.") }

用输出类型(Output Types)代表 Future 的参数

有时,一个耗时任务生成的结果需要作为一个参数传递给完成处理器闭包。要在 Combine 中实现同样的功能,就需要为这个参数声明 Future 发布的输出类型。

下方的示例可以生成一个随机整型数。它将 Future 发布的输出类型声明为 Int类型, 并将生成的整型值传递给 promise

func performAsyncActionAsFutureWithParameter() -> Future <Int, Never> {
return Future() { promise in
DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
let rn = Int.random(in: 1...10)
promise(Result.success(rn))
}
}
}

请注意,performAsyncActionAsFuture 方法的返回值是 Future <Void, Never> 类型,而 performAsyncActionAsFutureWithParameter 方法的返回值是 Future <Int, Never> 类型。

通过声明 Future 产生的元素为 Int 类型,Future 可以使用 Resultpromise 传入整型值。当 promise 执行结束时,Future 会发布这个值,然后调用方就可以通过订阅者(如:sink(receiveValue:))接收到这个值:

cancellable = performAsyncActionAsFutureWithParameter()
.sink() { rn in print("Got random number \(rn).") }

Subject 取代重复执行的闭包

你的应用也可能会采用这种常见的模式:将一个闭包作为一个属性,然后当某个事件发生时执行这个闭包属性。这种属性的命名通常以 on 开头,然后调用点看起来就像这样:

vc.onDoSomething = { print("Did something.") }

有了 Combine,你可以使用 Subject 替代这种模式。一个 Subject 实例允许你在任何时候通过调用 send() 方法来命令式地发布一个新元素。你可以通过使用私有的 PassthroughSubject 或者 CurrentValueSubject 来采用这种模式,然后将其作为一个 AnyPublisher 暴露给外部以便外部调用方使用它:

private lazy var myDoSomethingSubject = PassthroughSubject<Void, Never>()
lazy var doSomethingSubject = myDoSomethingSubject.eraseToAnyPublisher() // 发布一个空元组元素
myDoSomethingSubject.send(())

然后,调用方只需要在订阅者中执行对应的操作即可,不需要配置一个闭包属性:

cancellable = vc.doSomethingSubject
.sink() { print("Did something with Combine.") }

这种基于 Combine 的方法还有一个优势,subject 可以调用 send(completion:) 来告知订阅者:不会再有后续的事件发生,或者发生了一个错误。

总结

通过学习上述内容,我们可以感觉到:迁移现有的异步代码到 Combine 是比较容易的。而且,因为 Combine 提供了很多常用的操作符,它可以极大地提升我们的开发效率!

可以想象一下,以前写的很多方法/函数,现在只需要使用 Combine 就可以写成非常易读而且优雅的链式调用代码。如此一来,使用 Swift 进行开发的体验又会提升不少呢!

朋友,行动起来吧!把现有项目中的旧代码重构成使用 Combine 的代码~

本文内容来源:

Using Combine for Your App's Asynchronous Code

Combine 框架,从0到1 —— 4.在 Combine 中执行异步代码的更多相关文章

  1. Combine 框架,从0到1 —— 1.核心概念

      本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 1.核心概念.     内容概览 前言 核心概念 RxSwift Combine 总结 参考内容 ...

  2. Combine 框架,从0到1 —— 2.通过 ConnectablePublisher 控制何时发布

      本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 2.通过 ConnectablePublisher 控制何时发布.   内容概览 前言 使用 ma ...

  3. Combine 框架,从0到1 —— 3.使用 Subscriber 控制发布速度

      本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 3.使用 Subscriber 控制发布速度.   内容概览 前言 在发布者生产元素时消耗它们 使 ...

  4. Combine 框架,从0到1 —— 4.在 Combine 中使用通知

      本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 4.在 Combine 中使用通知.   内容概览 前言 让通知处理代码使用 Combine 总结 ...

  5. Combine 框架,从0到1 —— 4.在 Combine 中使用计时器

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 4.在 Combine 中使用计时器. 内容概览 前言 使用计时器执行周期性的工作 将计时器转换为计时 ...

  6. Combine 框架,从0到1 —— 4.在 Combine 中使用 KVO

      本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 4.在 Combine 中使用 KVO.   内容概览 前言 用 KVO 监控改动 将 KVO 代 ...

  7. Combine 框架,从0到1 —— 5.Combine 提供的发布者(Publishers)

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 5.Combine 提供的发布者(Publishers). 内容概览 前言 Just Future D ...

  8. Combine 框架,从0到1 —— 5.Combine 中的 Subjects

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 5.Combine 中的 Subjects. 内容概览 前言 PassthroughSubject C ...

  9. Combine 框架,从0到1 —— 5.Combine 常用操作符

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 5.Combine 常用操作符. 内容概览 前言 print breakpoint handleEve ...

随机推荐

  1. 细讲前端设置cookie, 储存用户登录信息

    细讲前端设置cookie 引言 正文 一.设置cookie 二.查看cookie 三.删除cookie 四.封装cookie操作 结束语 引言 我们都知道如果想做一个用户登录并使浏览器保存其登录信息, ...

  2. JavaScript正则、错误处理、操作表单

    一.正则表达式:用单个字符串描述或者匹配符合特定语句规则的字符串 一些字符序列组合在一起,可以简单也可以复杂模式的,可以去搜索,可以去替换 二.语法: /表达式/修饰符(可选) var para=/i ...

  3. 编译原理根据项目集规范族构造LR(0)分析表

    转载于https://blog.csdn.net/Johan_Joe_King/article/details/79058597?utm_medium=distribute.pc_relevant.n ...

  4. Spring Security认证流程分析--练气后期

    写在前面 在前一篇文章中,我们介绍了如何配置spring security的自定义认证页面,以及前后端分离场景下如何获取spring security的CSRF Token.在这一篇文章中我们将来分析 ...

  5. Mybatis动态语句

    If元素If元素是简单的条件判断逻辑,满足制定条件时追加if元素的SQL,不满足条件时不追加,使用格式如下: <select ….> SQL语句1 <if test=“条件表达式”& ...

  6. 产品经理培训教程视频大全与模板Axure rp9与8视频教程元件库模板

    注意:请仔细阅读购买,一旦发货百度网盘链接不能退~ 自动发邮件到买家留言处的邮箱,或注册淘宝时的邮箱自动通过旺旺给您发货还可以访问网页提取链接自助提取(复制到浏览器): http://4k5.cn/V ...

  7. SpringBoot输出日志到文件

    1 基本信息 SpringBoot版本2.2.5 日志框架SLF4J 日志框架的实现LockBack 2 输出文件的配置 2.1 logging.file.name 指定日志文件的位置. 2.1.1 ...

  8. ARDUBOY游戏开发之路(一) 初识ARDUBOY

    一.什么是ARDUBOY Arduboy是一个仅有信用卡大小的创造.分享游戏的开放平台.爱好者可以免费从Arduboy中选择一款经典的游戏,然后将游戏在目前最流行的arduino平台上编程.Ardub ...

  9. 起源seo为何要做seo培训

    http://www.wocaoseo.com/thread-91-1-1.html 焦大,在2010年末左右开始接触seo,2011年3月份正式开始做seo,到如今做seo已经3年了,实话说我没有其 ...

  10. 数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解

    数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解 对数组有不了解的可以先看看我的另一篇文章,那篇文章对数组有很多详细的解析,而本篇文章则着重讲动态数组,另一篇文章链接 ...