内容简介:独立博客ZYF.IM在 GitHub Trending 中总是看到环境:Swift 4.2、PromiseKit 6
独立博客ZYF.IM
在 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 的当前状态。
done
与 then
基本是一样的,但是它将不再返回 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-10 / 69.00元
本书以图配文,结合实例详细讲解了如何利用从网站上获取的各种数据了解网站的运营状况,如何从数据中攫取最有用的信息,如何优化站点,创造更大的网站价值。本书适合各类网站运营人员阅读。 第1 部分介绍了进行网站分析必备的基础知识。第2 部分详细讲解了如何明确网站现状,发现并改善网站的问题。第3 部分是关于流量获取和网站内渠道优化的问题。第4 部分介绍了一些更加先进的网站分析方法,其中详细讲解了如何分......一起来看看 《图解网站分析(修订版)》 这本书的介绍吧!