内容简介:独立博客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 入门使用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
How to Build a Billion Dollar App
George Berkowski / Little, Brown Book Group / 2015-4-1 / USD 24.95
Apps have changed the way we communicate, shop, play, interact and travel and their phenomenal popularity has presented possibly the biggest business opportunity in history. In How to Build a Billi......一起来看看 《How to Build a Billion Dollar App》 这本书的介绍吧!