Swift - 异步编程库PromiseKit使用详解1(安装配置、基本用法)
作者:hangge | 2018-12-04 08:10
有进行 JS 开发的小伙伴肯定对 Promise 很熟悉,作为 ES6 中最重要的特性之一,Promise 不但能有效地解决回调地狱(Callback Hell)问题,而且还能更好地进行错误捕获。我之前也写过相关文章进行介绍(点击查看)。而在 Swift 中,也有个实现类似功能的第三库,那就是 PromiseKit。
一、PromiseKit 的说明与配置
1,什么是 PromiseKit
(1)PromiseKit 是 iOS/MacOS 中一个用来处理异步编程的框架。
(2)PromiseKit 易学易用,大大简化了异步编程工作,让我们可以专注于更重要的事情。
(3)PromiseKit 不仅仅是 Promises 的实现,它还提供了许多辅助函数,以及相关扩展。方便我们开发工作。
2,安装与配置
(1)从 GitHub 上下载最新的代码:https://github.com/mxcl/PromiseKit
(2)将下载下来的源码包中 PromiseKit.xcodeproj 拖拽至你的工程中
(3)工程 -> General -> Embedded Binaries 项,把 PromiseKit.framework 添加进来。
(4)最后,在需要使用 PromiseKit 的地方 import 进来就可以了
import PromiseKit
二、基本用法
1,then()、done() 方法
- 简单来讲,then 方法就是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
- 而 Promise 的优势就在于这个链式调用。我们可以在 then 方法中继续写 Promise 对象并返回,然后继续调用 then 来进行回调操作。
- 不同于 then() 方法需要输入一个 promise 值并返回一个 promise,done() 方法可以输入一个 promise 值并返回一个空的 promise。因此我们会在整个链式调用的末尾使用 done() 方法做终结。
(1)下面通过样例作为演示,我们定义做饭、吃饭、洗碗(cook、eat、wash)这三个方法,它们是层层依赖的关系,下一步的的操作需要使用上一部操作的结果。(这里使用 DispatchQueue 创建延时队列来模拟异步操作)
//做饭 func cook() -> Promise<String> { print("开始做饭。") let p = Promise<String> { resolver in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("做饭完毕!") resolver.fulfill("鸡蛋炒饭") } } return p } //吃饭 func eat(data:String) -> Promise<String> { print("开始吃饭:" + data) let p = Promise<String> { resolver in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("吃饭完毕!") resolver.fulfill("一块碗和一双筷子") } } return p } //洗碗 func wash(data:String) -> Promise<String> { print("开始洗碗:" + data) let p = Promise<String> { resolver in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("洗碗完毕!") resolver.fulfill("干净的碗筷") } } return p }
(2)使用 then、done 链式调用这三个方法:
_ = cook() .then { data -> Promise<String> in return self.eat(data: data) }.then { data -> Promise<String> in return self.wash(data: data) }.done { data in print(data) }
当然上面代码还可以简化成如下:
_ = cook() .then(eat) .then(wash) .done { data in print(data) }
(3)运行结果如下:
2,catch() 方法
- 上面样例我们通过 fulfill 方法把 Promise 的状态置为完成态(fulfilled),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。
- 而 reject 方法就是把 Promise 的状态置为已失败(rejected),这时就能进到 catch 方法中,我们再次处理错误。
(1)下面同样使用一个样例做演示:
import UIKit import PromiseKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //链式调用 _ = cook() .then(eat) .done { data in print(data) }.catch{ error in print(error.localizedDescription + "没法吃!") } } //做饭 func cook() -> Promise<String> { print("开始做饭。") let p = Promise<String> { resolver in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("做饭失败!") let error = NSError(domain:"PromiseKitTutorial", code: 0, userInfo: [NSLocalizedDescriptionKey: "烧焦的米饭"]) resolver.reject(error) } } return p } //吃饭 func eat(data:String) -> Promise<String> { print("开始吃饭:" + data) let p = Promise<String> { resolver in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("吃饭完毕!") resolver.fulfill("一块碗和一双筷子") } } return p } }
(2)运行结果如下:
3,finally() 方法
在我们执行完 then,或者处理完 error 之后,还有一些操作(比如关闭活动指示器),那么就可以放到 finally 里面去执行。也就是说不管前面是 fulfill 还是 reject,最终都会进入到 finally 方法里来。
(1)这里将上面链式调用部分稍作改动,增加一个 finally:
//链式调用 _ = cook() .then(eat) .done { data in print(data) }.catch{ error in print(error.localizedDescription + "没法吃!") }.finally { print("出门上班") }
(2)运行结果如下:
4,map()、compactMap() 方法
- then() 方法要求输入一个 promise 值并返回一个 promise,而 map() 是根据先前 promise 的结果,然后返回一个新的对象或值类型。
- compactMap() 与 map() 类似,不过它是返回 Optional。比如我们返回 nil,则整个链会产生 PMKError.compactMap 错误。
(1)下面是一个 map() 的样例:
import UIKit import PromiseKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //链式调用 _ = cook() .map({ data -> String in return data + ",配上一碗汤" }) .then(eat) .done { data in print(data) } } //做饭 func cook() -> Promise<String> { print("开始做饭。") let p = Promise<String> { resolver in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("做饭完毕!") resolver.fulfill("鸡蛋炒饭") } } return p } //吃饭 func eat(data:String) -> Promise<String> { print("开始吃饭:" + data) let p = Promise<String> { resolver in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("吃饭完毕!") resolver.fulfill("一块碗和一双筷子") } } return p } }
(2)下面是一个 compactMap() 的样例:
fetchData() .compactMap { try JSONSerialization.jsonObject($0.data) as? [String] } .done { arrayOfStrings in //… } .catch { error in // Foundation.JSONError if JSON was badly formed // PMKError.compactMap if JSON was of different type }
5,get()、tap() 方法
如果想要在链路中获取值用于其他操作,比如输出调试。那么可以使用 get()、tap() 这两个方法,它们都不会影响到原有链路逻辑。
(1)get() 方法只有前面是完成状态(fulfilled)时才会调用,它得到的是具体结果对象:
_ = cook() .get{ data in print("---> \(data)") } .then(eat) .then(wash) .done {data in print(data) }
(2)tap() 方法是不管前面是完成(fulfilled)还是失败(rejected)都会调用,同时它得到的是 Result<T>:
_ = cook() .tap{ result in print("---> \(result)") } .then(eat) .done { data in print(data) }.catch{ error in print(error.localizedDescription + "没法吃!") }
6,when() 方法
- when 方法提供了并行执行异步操作的能力,并且只有在所有异步操作执行完后才执行回调。
- 和其他的 promise 链一样,when 方法中任一异步操作发生错误,都会进入到下一个 catch 方法中。
(1)比如下面代码,两个异步操作是并行执行的,等到它们都执行完后才会进到 done 里面。同时 when 会把所有异步操作的结果传给 done。
import UIKit import PromiseKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //链式调用 _ = when(fulfilled: cutUp(), boil()) .done{ result1, result2 in print("结果:\(result1)、\(result2)") } } //切菜 func cutUp() -> Promise<String> { print("开始切菜。") let p = Promise<String> { resolver in //做一些异步操作(延迟2秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) { print("切菜完毕!") resolver.fulfill("切好的菜") } } return p } //烧水 func boil() -> Promise<String> { print("开始烧水。") let p = Promise<String> { resolver in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("烧水完毕!") resolver.fulfill("烧好的水") } } return p } }
(2)运行结果如下:
7,race() 方法
- race 按字面解释,就是赛跑的意思。race 的用法与 when 一样,只不过 when 是等所有异步操作都执行完毕后才执行 then 回调。而 race 的话只要有一个异步操作执行完毕,就立刻执行 then 回调。
- 要注意的是,其它没有执行完毕的异步操作仍然会继续执行,而不是停止。
(1)这里我们将上面样例的 when 改成 race
import UIKit import PromiseKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //链式调用 _ = race(cutUp(), boil()) .done{ data in print("结果:\(data)") } } //切菜 func cutUp() -> Promise<String> { print("开始切菜。") let p = Promise<String> { resolver in //做一些异步操作(延迟2秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) { print("切菜完毕!") resolver.fulfill("切好的菜") } } return p } //烧水 func boil() -> Promise<String> { print("开始烧水。") let p = Promise<String> { resolver in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("烧水完毕!") resolver.fulfill("烧好的水") } } return p } }
(2)运行结果如下:
8,Guarantee
- Guarantee 是 Promise 的变种、或者补充,其用法和 Promise 一样,大多情况下二者可以互相替换使用。
- 与 Promise 状态可以是成功或者失败不同,Guarantee 要确保永不失败,因此语法也更简单些。
(1)这里我们将第一个样例中的 Promise 使用 Guarantee 替换:
import UIKit import PromiseKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() //链式调用 _ = cook() .then(eat) .then(wash) .done { data in print(data) } } //做饭 func cook() -> Guarantee<String> { print("开始做饭。") let g = Guarantee<String> { seal in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("做饭完毕!") seal("鸡蛋炒饭") } } return g } //吃饭 func eat(data:String) -> Guarantee<String> { print("开始吃饭:" + data) let g = Guarantee<String> { seal in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("吃饭完毕!") seal("一块碗和一双筷子") } } return g } //洗碗 func wash(data:String) -> Guarantee<String> { print("开始洗碗:" + data) let g = Guarantee<String> { seal in //做一些异步操作(延迟1秒执行) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { print("洗碗完毕!") seal("干净的碗筷") } } return g } }
(2)运行结果不变:
9,after() 方法
想要代码延迟一段时间执行,我们前面都是使用 DispatchQueue 创建延时队列来实现。其实 PromiseKit 已经为我们封装好了 after 这个延迟执行方法。
//延迟5秒执行 after(seconds: 5).done { print("欢迎访问hangge.com") }
全部评论(0)