微信号:DevTipss

介绍:程序猿的技术博客~ IOS ANDROID HTML5干货分享,不仅有技术还有人生~

Swift 如何优雅的实现 异步顺序任务

2016-04-21 16:53 sprint

在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


本文完~~

 
DevTips 更多文章 WEB开发资源整理 CocoaPods之~~~创建自己的依赖库 如何为ipa文件重新签名 10款Mac下高效率软件 使用 Redex 压缩和优化 Android APK
猜您喜欢 盘点开发者最喜爱的Swift技巧 编程不需要天份,也不需要热情。 iOS应用层架构之CDD(附Demo) 爱奇艺七月数据全线领跑 视频成移动端第三大门户 两届双十一间手机淘宝基础业务前端技术的演进