PromiseKit 入门使用

栏目: IOS · 发布时间: 5年前

内容简介:独立博客ZYF.IM在 GitHub Trending 中总是看到环境:Swift 4.2、PromiseKit 6

独立博客ZYF.IM

PromiseKit 入门使用

在 GitHub Trending 中总是看到 mxcl/PromiseKit 它是主要解决的是 “回调地狱” 的问题,决定尝试用一下。

环境:Swift 4.2、PromiseKit 6

then and done

下面是一个典型的 promise 链式(chain)调用:

firstly {
    login()
}.then { creds in
    fetch(avatar: creds.user)
}.done { image in
    self.imageView = image
}
复制代码

如果这段代码使用完成回调( completion handler )实现,他将是:

login { creds, error in
    if let creds = creds {
        fetch(avatar: creds.user) { image, error in
            if let image = image {
                self.imageView = image
            }
        }
    }
}
复制代码

then 是完成回调的另一种方式,但是它更丰富。在处级阶段的理解,它更具有可读性。上面的 promise chain 更容易阅读和理解:一个异步操作接着另一个,一行接一行。它与程序代码非常接近,因为我们很容易得到 Swift 的当前状态。

donethen 基本是一样的,但是它将不再返回 promise。它是典型的在末尾 “success” 部分的 chain。在上面的例子 done 中,我们接收到了最终的图片并使用它设置了 UI。

让我们对比一下两个 login 的方法签名:

// Promise:
func login() -> Promise<Creds>

// Compared with:
func login(completion: (Creds?, Error?) -> Void)
                    // 可选型,两者都是可选
复制代码

区别在于 promise,方法返回 promises 而不是的接受和运行回调。每一个处理器(handler)都会返回一个 promise。Promise 对象们定义 then 方法,该方法在继续链式调用之前等待 promise 的完成。chains 在程序上解决,一次一个 promise。

Promise 代表未来异步方法的输入值。它有一个表示它包装的对象类型的类型。例如,在上面的例子里, login 的返回 Promise 值代表一个 Creds 的一个实例。

可以注意到这与 completion pattern 的不同,promises chain 似乎忽略错误。并不是这样,实际上:promise chain 使错误处理更容易访问(accessible),并使错误更难被忽略。

catch

有了 promises,错误在 promise chain 上级联(cascade along),确保你的应用的健壮(robust)和清晰的代码。

firstly {
    login()
}.then { creds in
    fetch(avatar: creds.user)
}.done { image in
    self.imageView = image
}.catch {
    // 整个 chain 上的错误都到了这里
}
复制代码

如果你忘记了 catch 这个 chain,Swift 会发出警告

每个 promise 都是一个表示单个(individual)异步任务的对象。如果任务失败,它的 promise 将成为 rejected 。产生 rejected promises 将跳过后面所有的 then ,而是将执行 catch 。(严格上说是执行后续所有的 catch 处理)

这与 completion handler 对比:

func handle(error: Error) {
    //...
}

login { creds, error in
    guard let creds = creds else { return handle(error: error!) }
    fetch(avatar: creds.user) { image, error in
        guard let image = image else { return handle(error: error!) }
        self.imageView.image = image
    }
}
复制代码

使用 guard 和合并错误对处理有所保证,但是 promise chain 更具有可读性。

ensure

firstly {
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
    return login()
}.then {
    fetch(avatar: $0.user)
}.done {
    self.imageView = $0
}.ensure {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
}.catch {
    // ...
}
复制代码

无论在 chain 哪里结束,成功或者失败, ensure 终将被执行。也可以使用 finally 来完成相同的事情,区别是没有返回值。

spinner(visible: true)

firstly {
    foo()
}.done {
    // ...
}.catch {
    // ...
}.finally {
    self.spinner(visible: false)
}
复制代码

when

多个异步操作同时处理时可能又难又慢。例如当 操作1操作2 都完成时再返回结果:

// 串行操作
operation1 { result1 in
    operation2 { result2 in
        finish(result1, result2)
    }
}
复制代码
// 并行操作
var result1: ...!
var result2: ...!
let group = DispatchGroup()
group.enter()
group.enter()
operation1 {
    result1 = $0
    group.leave()
}
operation2 {
    result2 = $0
    group.leave()
}
group.notify(queue: .main) {
    finish(result1, result2)
}
复制代码

使人 Promises 将变得容易很多:

firstly {
    when(fulfilled: operation1(), operation2())
}.done { result1, result2 in
    // ...
}
复制代码

when 等待所有的完成再返回 promises 结果。

PromiseKit 扩展

PromiseKit 提过了一些 Apple API 的扩展,例如:

firstly {
    CLLocationManager.promise()
}.then { location in
    CLGeocoder.reverseGeocode(location)
}.done { placemarks in
    self.placemark.text = "\(placemarks.first)"
}
复制代码

同时需要指定 subspaces:

pod "PromiseKit"
pod "PromiseKit/CoreLocation"
pod "PromiseKit/MapKit"
复制代码

更多的扩展可以查询 PromiseKit organization ,甚至扩展了 Alamofire 这样的公共库。

制作 Promises

有时你的 chains 仍然需要以自己开始,或许你使用的三方库没有提供 promises 或者自己写了异步系统,没关系,他们非常容易添加 promises。如果你查看了 PromiseKit 的标准扩展,可以看到使用了下面相同的描述:

已有代码:

func fetch(completion: (String?, Error?) -> Void)
复制代码

转换:

func fetch() -> Promise<String> {
    return Promise { fetch(completion: $0.resolve) }
}
复制代码

更具有可读性的:

func fetch() -> Promise<String> {
    return Promise { seal in
        fetch { result, error in
            seal.resolve(result, error)
        }
    }
}
复制代码

Promise 初始化程序提供的 seal 对象定义了很多处理 garden-variety 完成回调的方法。

PromiseKit 设置尝试以 Promise(fetch) 进行处理,但是完成通过编译器的消歧义。

Guarantee

从 PromiseKit 5 开始,提供了 Guarantee 以做补充,目的是完善 Swift 强的的异常处理。

Guarantee 永远不会失败,所以不能被 rejected

firstly {
    after(seconds: 0.1)
}.done {
    // 这里不要加 catch
}
复制代码

Guarantee 的语法相较更简单:

func fetch() -> Promise<String> {
    return Guarantee { seal in
        fetch { result in
            seal(result)
        }
    }
}

// 减少为

func fetch() -> Promise<String> {
    return Guarantee(resolver: fetch)
}
复制代码

map compactMap 等

  • then 要求返回另一个 promise
  • map 要求返回一个 object 或 value 类型
  • compactMap 要求返回一个 可选型,如过返回 nil ,chain 将失败并报错 PMKError.compactMap
firstly {
    URLSession.shared.dataTask(.promise, with: rq)
}.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
}
复制代码

除此之外还有: thenMap compactMapValues firstValue etc

get

get 会得到 done 中相同值。

firstly {
    foo()
}.get { foo in
    // ...
}.done { foo in
    // same foo!
}
复制代码

tap

为 debug 提供 tap ,与 get 类似但是可以得到 Result<T> 这样就可以检查 chain 上的值:

firstly {
    foo()
}.tap {
    print($0)
}.done {
    // ...
}.catch {
    // ...
}
复制代码

补充

firstly

上面例子中的 firstly 是语法糖,非必须但是可以让 chains 更有可读性。

firstly {
    login()
}.then { creds in
    // ...
}

// 也可以
login().then { creds in
    // ...
}
复制代码

知识点: login() 返回了一个 Promise ,同时所有的 Promise 有一个 then 方法。 firstly 返回一个 Promise ,同样 then 也返回一个 Promise


以上所述就是小编给大家介绍的《PromiseKit 入门使用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网站运维技术与实践

网站运维技术与实践

饶琛琳 / 电子工业出版社 / 2014-3 / 69.00元

网站运维工作,一向以内容繁杂、覆盖面广著称。《网站运维技术与实践》选取日常工作涉及的监测调优、日志分析、集群规划、自动化部署、存储和数据库等方面,力图深入阐述各项工作的技术要点及协议原理,并介绍相关开源产品的实践经验。在技术之外,作者也分享了一些关于高效工作及个人成长方面的心得。 《网站运维技术与实践》适合Linux 系统管理员、中大型网站运维工程师及技术负责人、DevOps 爱好者阅读。同......一起来看看 《网站运维技术与实践》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具