聊聊 PromiseKit

swift 发布于 2017年11月20日
Promise 背景简介

Promise 这个概念之前更多应用于 JavaScript, 主要是为了解决 JavaScript 的 Callback Hell(回调地狱) 问题。 简单来说就是, JS 中很异步函数都是用 callback 的形式返回结果,而开发者经常会连续的使用这些异步调用,最后就导致回调嵌套的层级越来越深,严重影响代码的结构和可读性。

关于 Callback Hell, 还有一个外国朋友专门建立了一个页面 - http://callbackhell.com, 大家有兴趣的话可以了解一下。

Promise 的出现,就是用来解决 Callback Hell 这个问题的。它用线性调用的接口来封装回调嵌套这些问题, 在 JavaScript 中的应用比较广泛。 JS 有一个专门的第三方库就叫做 Promises:

https://www.promisejs.org

PromiseKit

介绍完了背景, 我们就来继续来了解 PromiseKit 吧。 PromiseKit 是专门针对 Swift 和 Objective-C 语言的解决方案。 我们先看看他能用来干什么。

如果用 iOS 原生的接口,我们要弹出一个 UIAlertView 对话框,并处理点击事件的话, 大概需要这样做:

func showAlert() {

    let alert = UIAlertView(title: "Title", message: "Msg", delegate: self, cancelButtonTitle: "Cancel", otherButtonTitles: "OK")
    alert.show()

}

func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {

    print("button index \(buttonIndex)")

}

我们先要创建一个 UIAlertView, 然后还要指定它的代理, 如果我们需要一些中间变量的话, 还需要将这些变量声明为类的属性, 以便这两个方法都能从全局来访问。 我们开发 iOS App 的时候,这种情况非常多。

那么咱们再来看看用 PromiseKit 来处理这个问题的方法:

import PromiseKit

func testAlert() {

    let alert = UIAlertView(title: "Title", message: "Msg", delegate: nil, cancelButtonTitle: "Cancel", otherButtonTitles: "OK")

    alert.promise().then { buttonIndex in

        print("ok \(buttonIndex)")

    }

}

这次我们没有用任何的代理方法, 而是直接用 PromiseKit 对 UIAlertView 提供的扩展, 在一个方法中将 UIAlertView 的创建和事件处理声明完成。

PromiseKit 对很对 iOS 提供的控件都提供了类似的扩展, 比如 UIActionSheet, NSURLConnection, UIViewController, CLLocationManager 等等。

链式调用

链式调用是 PromiseKit 的核心特性,比如这样的代码:

NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://aaa.cc")!) { data, response, error in

    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://bbb.cc")!) { data, response, error in

        NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://ddd.cc")!) { data, response, error in

            print("finished")

        }

    }

}

这个就是 Callback Hell, dataTaskWithURL 方法的回调闭包一级一级的嵌套。 现在我们这个示例中每个闭包没有太多代码量, 但在实际的项目中,往往这些嵌套的闭包中都含有大量的代码,这样就会严重影响我们阅读这些代码。

再来看看 PromiseKit 的处理方式:

NSURLSession.sharedSession().promise(NSURLRequest(URL: NSURL(string: "http://aaa.cc")!)).then { data in

    return NSURLSession.sharedSession().promise(NSURLRequest(URL: NSURL(string: "http://bbb.cc")!))

}.then { data in

    return NSURLSession.sharedSession().promise(NSURLRequest(URL: NSURL(string: "http://ddd.cc")!))

}.then { data in

    print("finished")

}

这个就是 Promise 的处理方式了, 它的每一层闭包中,我们可以再返回一个 Promise 对象,然后本来是层级嵌套的逻辑, 就变成了线性的逻辑了。 当代码量变大的时候, 我们依然能够很方便的分清楚代码的逻辑结构。

when

有些时候,我们可能会依赖于某些异步任务,需要等他们执行完成后,再进行一些操作。 PromiseKit 为我们提供了这样方便的接口, 比如这样:

let req1 = NSURLSession.sharedSession().promise(NSURLRequest(URL: NSURL(string: "http://bbb.cc")!))
let req2 = NSURLSession.sharedSession().promise(NSURLRequest(URL: NSURL(string: "http://ddd.cc")!))

when(req1, req2).then { resp1, resp2 in

}.error { err in

}

PromiseKit 的 when 函数, 会等到 req1 和 req2 两个请求执行完毕后,在执行后面的闭包,如果期间发生错误, error 就会被调用。

结尾

以上就是 PromiseKit 的基本介绍了, 关于它的更多内容,可以参看 Github 主页:

https://github.com/mxcl/PromiseKit

如果你之前有过其他平台 Promise 的使用经验, 相信上手应该不难。 它确实可以让你的代码简单很多,当然至于是否要采用这种风格,也是见仁见智了。这里抛砖引玉,给大家多提供一种选择。


如果你觉得这篇文章有帮助,还可以关注微信公众号 swift-cafe,会有更多我的原创内容分享给你~

本站文章均为原创内容,如需转载请注明出处,谢谢。
关注微信公众号
发现更多精彩
swift-cafe