Promise

让“回调地狱”滚蛋吧!

Promise_第1张图片
timg.jpg

目前很多语言都支持了异步编程,比如 ES6 中新加的特性,它很好的支持了 Promise。相信不久后,Swift 也会支持这种异步编程的方式。不过目前有 PromiseKit , 它很好的实现了 Promise。使用它完全可以避免“回调地狱”这种问题。

我现在有这么一个需求,先登录,登录后获取用户id,根据用户id 获取好用列表,然后获取动态列表。如果使用 Promise 的方式,是这样的:

func request() {
    // 1. 登录
    let logPormise = NetWork.postWithParams(params: nil, urlStr: loginUrl)
    
    logPormise.then { (result) -> Promise<[String: Any]> in
    
        // 1. 获取好友列表
        return NetWork.postWithParams(params: nil, urlStr: contactUrl)
        
        }.then { (result) -> Promise<[String: Any]> in
            // 3. 获取动态列表
            return NetWork.postWithParams(params: nil, urlStr: trendUrl)
            
        }.then { result in
            
        }.catch { error in
            print("Network error:", error)
    }
}

那么什么是 Promise 呢?它翻译过来是“承诺”。它是异步编程的一种解决方案,使用它可以解决“回调地狱”的问题。从语法上来讲,Promise 是一个对象。 它主要有一下两个特点:

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

如何开启 Demo

介绍 PromiseKit 时,lefe 写了一个 Demo,可以在这里下载 PromiseKitDemo 。

如果你想使用本例的接口,可以使用 TCZNodeServer 搭建一个服务器。这样能更容易的理解。比如我想让网络请求返回 code 值不为 200,这样表示网络异常,当然 Promise 就会走 catch

module.exports = function (req, res) {
    var result = {
        code: 200,
        data: {
            userId: "123456"
        }
    }
    res.json(result);
}

构建 Promise

Promise 接收两个参数,fulfill 和 reject,它们是两个闭包,在 JS 中也就是两个函数,这样可能更好的理解。fulfill 的作用是从“未完成”变成“成功”(即从 Pending 变为 Resolved);reject 的作用是从“未完成”变成“失败”(即从 Pending 变为 Rejected)。Promise 一旦创建后就执行了
let logPormise = NetWork.postWithParams(params: nil, urlStr: loginUrl) 那么这个 logPormise 已经开始执行了。

required public init(resolvers: (@escaping (T) -> Swift.Void, @escaping (Error) -> Swift.Void) throws -> Swift.Void)

Then

then 表示还可以做什么,它的作用是为 Promise 实例添加状态改变时的回调函数。下面的例子中一旦登录成功将执行 then 后的 block ,如果有异常发生,将执行 catch,捕获异常。

let logPormise = NetWork.postWithParams(params: nil, urlStr: loginUrl)
logPormise.then(execute: { (result) -> Promise in
     return Promise(value: result)
}).catch { error in
            
}

Catch

当 Promise 变为 reject 后调用,也就是用来捕获 Promise 的异常。

When

当所有被指定的 Promise 发生后,剩余的 Promise 才会发生。比如有这么个场景,只有用户登录,和获取联系人列表后才能获取动态列表;

func requestWhen() {
   let logPormise = NetWork.postWithParams(params: nil, urlStr: loginUrl)
   let contactPormise = NetWork.postWithParams(params: nil, urlStr: contactUrl)
        
   let resultPromise = when(resolved: [logPormise, contactPormise])
        
   resultPromise.then { (results) -> Promise<[String: Any]> in
        print(results)
        return NetWork.postWithParams(params: nil, urlStr: trendUrl)
            
   }.then { (trends) -> Promise in
        print(trends)
        return Promise(value: "Success")
   }.catch { (error) in
        print(error)
  }
}

运行结果为:

quest url:  http://192.168.199.140:3000/api/login/login
Request url:  http://192.168.199.140:3000/api/login/contactList
Request url:  http://192.168.199.140:3000/api/login/trendList
[PromiseKit.Result>.fulfilled(["code": 200, "data": {
    userId = 123456;
}]), PromiseKit.Result>.fulfilled(["code": 200, "data": <__NSCFArray 0x17026f140>(
{
    nickName = Lefe;
    userId = 123456;
}
)
])]

["code": 200, "data": <__NSCFArray 0x17406e340>(
{
    title = PromiseKit;
    trendId = 123456;
}
)
]

上面的例子中只有 logPormise 和 contactPormise 的状态都为 fulfilled,resultPromise 的状态才能为 fulfilled;只要 logPormise 和 contactPormise 有一个为 rejected,那么 resultPromise 的状态的 rejected。将会执行 catch 后的 block。

race

上面代码中,只要logPormise 和 contactPormise之中有一个实例率先改变状态,resultPromise 的状态就跟着改变。当一堆承诺兑现时返回第一个承诺。也就是,第一个完成的胜出。就像比赛一样,当 3 个人赛跑时,第一名胜出,Promise 的状态就会改变。

func requestRace() {
   let logPormise = NetWork.postWithParams(params: nil, urlStr: loginUrl)
   let contactPormise = NetWork.postWithParams(params: nil, urlStr: contactUrl)
        
   let resultPromise = race(resolved: [logPormise, contactPormise])
        
   resultPromise.then { (results) -> Promise<[String: Any]> in
        print(results)
        return NetWork.postWithParams(params: nil, urlStr: trendUrl)
            
   }.then { (trends) -> Promise in
        print(trends)
        return Promise(value: "Success")
   }.catch { (error) in
        print(error)
  }
}

运行结果为:(与 when 运行的结果做一个对比,这里的请求只有返回两个回调)

Request url:  http://192.168.199.140:3000/api/login/login
Request url:  http://192.168.199.140:3000/api/login/contactList
Request url:  http://192.168.199.140:3000/api/login/trendList
["code": 200, "data": {
    userId = 123456;
}]
["code": 200, "data": <__NSCFArray 0x1702686c0>(
{
    title = PromiseKit;
    trendId = 123456;
}
)
]

Join

when 和 join 都是在指定的 Promise 被兑现之后调用。只是 rejected 块有所不同。join 在拒绝之前总是等待所有的承诺完成,看它们之中是否有被拒绝的。而 when(fulfilled:) 只要有任何一个承诺被拒绝它就拒绝。

Always

不管前面的 Promise 是 fulfilled 还是 rejected,always 的回调会一直调用

func requestAlways() {
    let logPormise = NetWork.postWithParams(params: nil, urlStr: loginUrl)
    logPormise.then { (results) in
       print(results)
            
    }.always(execute: { 
       print("always")
    })
    .catch { (error) in
       print(error)
   }
}

OC 使用方法

OC 使用的是 AnyPromise,通过方法

[AnyPromise promiseWithResolverBlock:^(void (^ resolve)(id _Nullable)) {
    // resolve(param) 当 param 是 error 时调用相当于 reject,否则为 fullfilled
}
AnyPromise *logPrimise = [NetWork postWithParams:nil url:loginUrl];
logPrimise.then(^(NSDictionary *resultDict){
    NSLog(@"%@", resultDict);
    return [NetWork postWithParams:nil url:contactUrl];
}).then(^(NSDictionary *result){
    NSLog(@"%@", result);
    return [NetWork postWithParams:nil url:trendUrl];
}).then(^(NSDictionary *result){
    NSLog(@"%@", result);
}).catch(^(NSError *error){
    NSLog(@"%@", error);
});

参考

博客

ES6

Eliyar's Blog

Onevcat

===== 我是有底线的 ======
喜欢我的文章,欢迎关注我的新浪微博 Lefe_x,我会不定期的分享一些开发技巧

Promise_第2张图片

你可能感兴趣的:(Promise)