返回 导航

Swift

hangge.com

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 值并返回一个 promisedone() 方法可以输入一个 promise 值并返回一个空的 promise。因此我们会在整个链式调用的末尾使用 done() 方法做终结。

(1)下面通过样例作为演示,我们定义做饭、吃饭、洗碗(cookeatwash)这三个方法,它们是层层依赖的关系,下一步的的操作需要使用上一部操作的结果。(这里使用 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)使用 thendone 链式调用这三个方法:
_ = 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)

回到顶部