Swift 如何优雅的实现 异步顺序任务
在Swift语言中 RxSwift PromiseKit 是比较有代表性的FRP框架 他们都提供了异步顺序执行方案 即使你不了解他们 也不会影响你阅读本篇文章 此教程将会带你一步步实现异步顺序任务
为什么要实现 异步顺序任务?我们将举例展开我们今天的话题
需求:
①登录 ②获取用户信息 ③获取用户好友列表 依次顺序异步完成三个操作 该如何实现?
常规实现
常规的写法可能如下:
//登录任务
func asyncLoginTask(complete : ()-> Void) {
//..do something
print("asyncLoginTask")
complete()
}
//获取用户信息
func asyncGetUserInfoTask(complete : ()-> Void) {
//..do something
print("asyncGetUserInfoTask")
complete()
}
//获取用户好友列表
func asyncGetFriendsLoginTask(complete : ()-> Void) {
//..do something
print("asyncGetFriendsLoginTask")
complete()
}
//调用
asyncLoginTask {
asyncGetUserInfoTask({
asyncGetFriendsLoginTask({
})
})
}
是不是感觉身处地狱?如果再有更深层次调用..... 不敢想象
这种代码仅耦合度过高 而且其他开发者维护时将无从下手 随着时间的推移 它们将成为僵尸代码 无人敢碰
为了避免这种事情发生 我们不得不重构它们
重构
从上述代码可以看到 asyncLoginTask asyncGetUserInfoTask asyncGetFriendsLoginTask 都是高阶函数 它们都接收另外一个函数作为参数 complete参数就是任务完成后执行的操作 就像下边的定义一样
typealias AsyncTask = (()->Void) -> Void
我们定义了AsyncTask函数类型 他代表一个异步任务
asyncLoginTask等其他函数就是AsyncTask
那么我们改如何将任务串联一起顺序执行呢?
我们可以定义一个函数 用于连接它们
func contact(l:AsyncTask,r:AsyncTask) -> Void {
l{
print("l 任务执行完成")
r{
print("r 任务执行完成")
}
}
}
contact故名此意就是连接的意思 它将连接l 和 r 依次执行
contact的实现看起来有点奇怪? 这个一会我们将会改进
那么如何调用呢?
contact(asyncLoginTask, r: asyncGetUserInfoTask)
很简单 asyncLoginTask 和 asyncGetUserInfoTask签名与
AsyncTask一致 所以可以直接作为参数传入
你可以在控制台看到如下内容:
asyncLoginTask
l 任务执行完成
asyncGetUserInfoTask
r 任务执行完成
我们顺利的完成了两个任务的连接
定义+>操作符
Swift中允许我们定义操作符
为了简化调用 我们使用 +> 作为连接操作符
+> 并不存在 我们需要先声明
infix operator +> {}
** 实现 +>操作符**
func +>(l:AsyncTask,r:AsyncTask) -> Void {
l{
r{
}
}
}
调用
asyncLoginTask +> asyncGetUserInfoTask
控制台输出:
asyncLoginTask
asyncGetUserInfoTask
我们同样实现了顺序执行
可是.. 当我们试图连接第三个任务时 将会出现编译错误
asyncLoginTask +> asyncGetUserInfoTask +> asyncGetFriendsLoginTask
这不是我们想要的...
如果你查看+>操作符的定义 你将会找到错误的原因
+>操作符的返回值为Void
所以asyncLoginTask +> asyncGetUserInfoTask的返回值为Void
当我们试图 连接三个任务时
asyncLoginTask +> asyncGetUserInfoTask +> asyncGetFriendsLoginTask
他们被解析为:
Void +> asyncGetFriendsLoginTask
额??? 我们并未给 Void +> AsyncTask定义这种操作符
很简单 我们将 +> 操作符的返回值改为 AsyncTask 即可
重新实现 +>操作符
func +>(l:AsyncTask,r:AsyncTask) -> AsyncTask {
return { complete in
l{
r{
complete()
}
}
}
}
接下来我们使用 +> 连接任务
let task = asyncLoginTask +> asyncGetUserInfoTask +> asyncGetFriendsLoginTask
task {
print("执行完成")
}
编译器再次出现编译错误
Non-associative operator is adjacent to operator of same .....
原因是我们未给+>定义 associativity
它将不知道操作符的计算优先级 由于我们的任务是从左依次执行 所以我们需要修改 +>定义
infix operator +> {
associativity left
precedence 160
}
保存代码 这时候控制台输出如下内容
asyncLoginTask
asyncGetUserInfoTask
asyncGetFriendsLoginTask
执行完成
你发现了什么?我们使用+>连接任务时 使用 let task常量接受返回值
task的类型是AsyncTask 这意味者我们可以无限连接
let task = asyncLoginTask +> asyncGetUserInfoTask +> asyncGetFriendsLoginTask +> asyncLoginTask
task {
print("执行完成")
}
控制台输出:
asyncLoginTask
asyncGetUserInfoTask
asyncGetFriendsLoginTask
asyncLoginTask
执行完成
是不是很刺激~~~
再次改进
asyncLoginTask等任务的回调参数complete的类型都为()->Void
这意味着我们可以使用typealias定义如下类型
完整代码如下:
typealias Action = ()->Void
typealias AsyncTask = (Action) -> Void
infix operator +> {
associativity left
precedence 160
}
func +>(l:AsyncTask,r:AsyncTask) -> AsyncTask {
return { complete in
l{
r{
complete()
}
}
}
}
//登录任务
func asyncLoginTask(complete : Action) {
//..do something
print("asyncLoginTask")
complete()
}
//获取用户信息
func asyncGetUserInfoTask(complete :Action) {
//..do something
print("asyncGetUserInfoTask")
complete()
}
//获取用户好友列表
func asyncGetFriendsLoginTask(complete : Action) {
//..do something
print("asyncGetFriendsLoginTask")
complete()
}
let task = asyncLoginTask +> asyncGetUserInfoTask +> asyncGetFriendsLoginTask +> asyncLoginTask
task {
print("执行完成")
}
是不是发现和之前的相比 这种代码更易读
假如日后需求需要调整调用顺序 我们只需要换个位置即可
而不需要调整太多代码
为了保证教程易懂 我们并没有处理错误 也没有对Action添加输入参数 如果你理解了上述代码 可以试着添加这些功能
欢迎关注个人公众号:DevTipss
微信公众号:DevTipss本文完~~