PromiseKit - 尝试函数式编程

最近开发中遇到了一些场景需要这样场景,先登录、根据拿到用户 token 去请求用户信息,再根据用户信息做别的操作。一般用回调去解决,但是这个会造成回调地狱。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Alamofire.request("login").responseJSON { loginResponse in
if loginResponse.result.isSuccess {
Alamofire.request("userInfo").responseJSON { userInfoResponse in
if userInfoResponse.result.isSuccess {
Alamofire.request("friendList").responseJSON { userInfoResponse in
if userInfoResponse.result.isSuccess {
// Update UI
} else {
print("friendList request failed")
}
}
} else {
print("userInfo request failed")
}
}
} else {
print("login request failed")
}
}

解决方案

主要解决方案有 RxSwiftPromiseKit。 如果仅仅为了解决回调地狱,建议选择 PromiseKit。具体原因请看: RxSwift vs PromiseKit

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

链式调用 then

上面的回调地狱可以使用 PromiseKit 拆解成几个函数,变成线性的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
func loadWithPromise() {
login()
.then { token -> Promise<String> in
print("登录成功")
return self.userInfo(token: token)
}.then { info -> Promise<[String]> in
print("获取用户信息成功")
return self.friendList(info: info)
}.then { list in
print("获取好友列表成功")
}.catch { (error) in
// 错误处理
print(error)
}
}

func login() -> Promise<String> {
return Promise<String> { fulfill, reject in
Alamofire.request("login").responseJSON { loginResponse in
if loginResponse.result.isSuccess {
// 获取成功时候调用 fullfill
fulfill("token")
} else {
reject(loginResponse.result.error!)
}
}
}
}

func userInfo(token: String) -> Promise<String> {
return Promise<String> { fulfill, reject in
Alamofire.request("userInfo", parameters: ["token": token]).responseJSON { loginResponse in
if loginResponse.result.isSuccess {
fulfill("info")
} else {
reject(loginResponse.result.error!)
}
}
}
}

func friendList(info: String) -> Promise<[String]> {
return Promise<[String]> { fulfill, reject in
Alamofire.request("friendList", parameters: ["info": info]).responseJSON { loginResponse in
if loginResponse.result.isSuccess {
fulfill(["a","b"])
} else {
reject(loginResponse.result.error!)
}
}
}
}

when

经常会有这种需求,当 req1, req2 俩请求都完成后,再进行某个操作。则可以这么写:

1
2
3
4
5
6
7
8
9
let req1 = userInfo(token: "token")
let req2 = friendList(info: "info")

when(fulfilled: req1, req2)
.then { data, location -> Void in
// both tasks suceeded!
}.catch { error in
// at least one of the tasks failed
}

more

Cocoa+Promise 已经扩展了大部分 Cocoa 框架,比如 CLLocationManager,如获取 location 并且 req1 请求结束后进行某个操作,则可以这么写:

1
2
3
4
5
6
7
8
9
let req1    = userInfo(token: "token")
let locater = CLLocationManager.promise()

when(fulfilled: req1, locater)
.then { info, location in
// both tasks suceeded!
}.catch { error in
// at least one of the tasks failed
}

更多请参考官方文档。

参考