承接上一篇,轻松无痛实现异步操作串行。 如果没看过上一篇,阅读本篇可能会有点懵逼。
在上一篇文章中,我主要描述了如何实现异步串行运算符,+>
。并演示了如何基于他来做一些诸如参数的传递和错误的处理等操作。
这篇文章中,我们会基于之前的发现,来实现异步并行运算符 。 以及基于
+>
和 来做一些有趣的应用。
本文的主要内容:
- 实现并行折叠运算符:
;
- 基于
+>
和,实现一个简洁优雅的 Promise 接口;
第一部分 能够折叠异步并行操作的运算符
什么是折叠
首先,我们需要定义什么是异步并行? 就是我们同时执行多个异步操作,当所有操作都执行完毕后,执行异步(Complete)回调。比如我们已经有了用户的 ID,需要同时请求用户的头像和基本资料。在两个请求都拿到数据时,刷新界面。
在上一篇文章中,我们在提出运算符 +>
之前,提出了一个连接的概念。指的是把两个异步操作连接起来,一个执行完就执行另一个。通过连接,把两个异步操作合并为一个。
但现在异步并行,显然不能用连接,因为多个请求是一起发生的,没有先后顺序。在本文中,用折叠来表示把多个异步请求以并行的方式合并为一个的过程。
基本分析
首先,回忆一下我们异步串行运算符的签名:
1 2 3 |
typealias AsyncFunc = (info : AnyObject,complete:(AnyObject?,NSError?)->Void) -> Void +> : (AsyncFunc,AsyncFunc) -> AsyncFunc |
我们通过实现把两个异步操作折叠为一个,来实现串行折叠任意多个异步操作。
并行的思路也是一样的,我们只要实现并行折叠两个异步操作,我们就能折叠任意多个异步操作。
我们首先写出函数的签名:
1 |
func (left : AsyncFunc, right : AsyncFunc) -> AsyncFunc |
为什么我们选择的串行异步运算符
+>
是非对称的,而并行异步运算符却是对称的呢?这还是由串行异步和并行异步两个运算的性质决定的,串行异步不满足交换律,因为串行就代表了运算本身有先后。而并行却没这个限制。
a b == b a
,但a +> b != b +> a
按照惯例,我们先根据函数的签名(返回一个函数),撸个基本的架子:
1 2 3 4 5 |
func (left : AsyncFunc , right : AsyncFunc) -> AsyncFunc{ return { info, complete in } } |
架子搭好以后,我们来思考一下如何实现函数体, 有以下几个方面
这里的函数体,是指我们
return
后面的函数的函数体,而不是的函数体,如果一味思考后者,很容易懵逼。函数式编程的一个关键技巧就是通过类型来拆分抽象层次,局部具体,总体抽象。
- 主体逻辑
既然我们的是用来把两个异步操作并行折叠成一个,所以我们返回的函数体要实现的功能就是同时执行
left
和right
这两个函数,当两个函数都执行完毕后(两者都调用了自己的 complete 闭包),再调用最外层的 complete 闭包,也就是我们返回的函数签名的第二个参数。
- 参数传递
最外层的参数info
, 代表总的输入参数。需要分别在调用left
和right
时传给它们。那如何表达并行折叠后的异步调用的结果呢?我们知道left
和right
作为类型为AsyncFunc
的异步函数,在它们调用自己的complete
闭包时都会带上自己的结果。其中一种可选的方式就是把left
和right
的结果通过数组合并,当做折叠后的异步的结果。
实现异步折叠运算符
基于以上的分析,我们大概可以给出如下的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
func (left : AsyncFunc , right : AsyncFunc) -> AsyncFunc{ return { info, complete in var leftComplete = false var rightComplete = false var leftResult:AnyObject? = nil var rightResult:AnyObject? = nil let checkComplete = { if leftComplete && rightComplete{ let finalResult:[AnyObject] = [leftResult!, rightResult!] complete(finalResult, nil) } } left(info: info){result,error in guard error == nil else{ complete(nil, error) return } leftComplete = true leftResult = result; checkComplete() } right(info: info){result,error in guard error == nil else{ complete(nil, error) return } rightComplete = true rightResult = result; checkComplete() } } } |
上面的代码逻辑其实很简单,我们通过一个 checkComplete
函数来检查两个任务是否都已经完成,如果完成则合并两个异步函数返回的结果,并调用最外层的 complete
闭包。 两个异步函数则直接调用,在 complete
闭包中检查是否出错,没有则保存相应的结果,和置对应的标志位。
测试一下
承接上一篇,轻松无痛实现异步操作串行。 如果没看过上一篇,阅读本篇可能会有点懵逼。
在上一篇文章中,我主要描述了如何实现异步串行运算符,+>
。并演示了如何基于他来做一些诸如参数的传递和错误的处理等操作。
这篇文章中,我们会基于之前的发现,来实现异步并行运算符 。 以及基于
+>
和 来做一些有趣的应用。
本文的主要内容:
- 实现并行折叠运算符:
;
- 基于
+>
和,实现一个简洁优雅的 Promise 接口;
第一部分 能够折叠异步并行操作的运算符
什么是折叠
首先,我们需要定义什么是异步并行? 就是我们同时执行多个异步操作,当所有操作都执行完毕后,执行异步(Complete)回调。比如我们已经有了用户的 ID,需要同时请求用户的头像和基本资料。在两个请求都拿到数据时,刷新界面。
在上一篇文章中,我们在提出运算符 +>
之前,提出了一个连接的概念。指的是把两个异步操作连接起来,一个执行完就执行另一个。通过连接,把两个异步操作合并为一个。
但现在异步并行,显然不能用连接,因为多个请求是一起发生的,没有先后顺序。在本文中,用折叠来表示把多个异步请求以并行的方式合并为一个的过程。
基本分析
首先,回忆一下我们异步串行运算符的签名:
1 2 3 |
typealias AsyncFunc = (info : AnyObject,complete:(AnyObject?,NSError?)->Void) -> Void +> : (AsyncFunc,AsyncFunc) -> AsyncFunc |
我们通过实现把两个异步操作折叠为一个,来实现串行折叠任意多个异步操作。
并行的思路也是一样的,我们只要实现并行折叠两个异步操作,我们就能折叠任意多个异步操作。
我们首先写出函数的签名:
1 |
func (left : AsyncFunc, right : AsyncFunc) -> AsyncFunc |
为什么我们选择的串行异步运算符
+>
是非对称的,而并行异步运算符却是对称的呢?这还是由串行异步和并行异步两个运算的性质决定的,串行异步不满足交换律,因为串行就代表了运算本身有先后。而并行却没这个限制。
a b == b a
,但a +> b != b +> a
按照惯例,我们先根据函数的签名(返回一个函数),撸个基本的架子:
1 2 3 4 5 |
func (left : AsyncFunc , right : AsyncFunc) -> AsyncFunc{ return { info, complete in } } |
架子搭好以后,我们来思考一下如何实现函数体, 有以下几个方面
这里的函数体,是指我们
return
后面的函数的函数体,而不是的函数体,如果一味思考后者,很容易懵逼。函数式编程的一个关键技巧就是通过类型来拆分抽象层次,局部具体,总体抽象。
- 主体逻辑
既然我们的是用来把两个异步操作并行折叠成一个,所以我们返回的函数体要实现的功能就是同时执行
left
和right
这两个函数,当两个函数都执行完毕后(两者都调用了自己的 complete 闭包),再调用最外层的 complete 闭包,也就是我们返回的函数签名的第二个参数。
- 参数传递
最外层的参数info
, 代表总的输入参数。需要分别在调用left
和right
时传给它们。那如何表达并行折叠后的异步调用的结果呢?我们知道left
和right
作为类型为AsyncFunc
的异步函数,在它们调用自己的complete
闭包时都会带上自己的结果。其中一种可选的方式就是把left
和right
的结果通过数组合并,当做折叠后的异步的结果。
实现异步折叠运算符
基于以上的分析,我们大概可以给出如下的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
func (left : AsyncFunc , right : AsyncFunc) -> AsyncFunc{ return { info, complete in var leftComplete = false var rightComplete = false var leftResult:AnyObject? = nil var rightResult:AnyObject? = nil let checkComplete = { if leftComplete && rightComplete{ let finalResult:[AnyObject] = [leftResult!, rightResult!] complete(finalResult, nil) } } left(info: info){result,error in guard error == nil else{ complete(nil, error) return } leftComplete = true leftResult = result; checkComplete() } right(info: info){result,error in guard error == nil else{ complete(nil, error) return } rightComplete = true rightResult = result; checkComplete() } } } |
上面的代码逻辑其实很简单,我们通过一个 checkComplete
函数来检查两个任务是否都已经完成,如果完成则合并两个异步函数返回的结果,并调用最外层的