谈一谈Promise

栏目: IOS · 发布时间: 5年前

内容简介:本文将会向你简单介绍promise,并在最后尝试使用OC实现一个可用的promise库。在面向对象的世界promise也并不特殊。promise对象表示异步操作的最终完成(或失败)及其结果值。这样一句话显然无法让人理解promise,它是做什么的?要怎么使用它?为什么要使用它?下面的内容将会解释这些问题。即使现在完全不理解promise是什么,请先记住“promise”这个字面含义给你带来的暗示:某件事情(代码)在未来的某个时刻发生(执行)。

本文将会向你简单介绍promise,并在最后尝试使用OC实现一个可用的promise库。

什么是promise?

在面向对象的世界promise也并不特殊。promise对象表示异步操作的最终完成(或失败)及其结果值。

这样一句话显然无法让人理解promise,它是做什么的?要怎么使用它?为什么要使用它?下面的内容将会解释这些问题。即使现在完全不理解promise是什么,请先记住“promise”这个字面含义给你带来的暗示:某件事情(代码)在未来的某个时刻发生(执行)。

我们遇到的问题

通常我们使用block回调来处理一些异步操作,比如:

[object doSomethingWithArg:arg handler:^(id data){  
    //resolve data
}];
复制代码

上面的代码没有问题,然而假设我们遇到了如下场景:发起网络请求A>获取网络请求A的返回数据A>使用返回数据A作为参数发起网络请求B…如此往复,通常还包含着网络错误的回调,代码看起来是这样的:

//ViewController.m
- (void)viewDidLoad {
    [[XXNetwork shared] requestAWithArg:arg success:^(id dataA){  
        [[XXNetwork shared] requestBWithArg:dataA success:^(id dataB){  
            [[XXNetwork shared] requestCWithArg:dataB success:^(id dataC){  
                    //resolve dataC
            } failure:^(NSError *error){
                //网络错误处理
            }];
        } failure:^(NSError *error){
            //网络错误处理
        }];
    } failure:^(NSError *error){
        //网络错误处理
    }];
}

//XXNetwork.h
//异步的网络请求
- (void)requestAWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure;
- (void)requestBWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure;
- (void)requestCWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure;


//XXNetwork.m
//原有的方法
- (void)requestAWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure {
	//doSomething
}
...
复制代码

上面代码看起来还算友好,但是当这样的嵌套太深的时候,问题就出现了:

1.过多的嵌套造成代码无法被轻松的阅读

2.网络错误的状况没有统一的处理

这样场景下使用block回调嵌套显然不够优雅,那么我们要怎么做?

使用promise改造代码

对于我们遇到的问题,promise将会大显身手,使用promise改造后代码看起来大概是这样的:

//ViewController.m
- (void)viewDidLoad {
    [[XXNetwork shared] requestAWithArg:arg].then(^id(id value){
        //value 即 dataA
        return [[XXNetwork shared] requestBWithArg:value];
    }).then(^id(id value){
        //value 即 dataB
        return [[XXNetwork shared] requestCWithArg:value];
    }).then(^id(id value){
        //value 即 dataC
        //resolve dataC
    }).catch(^id(NSError * error){
        //网络错误处理
    });
}



//XXNetwork.h
//原有的方法
//异步的网络请求
- (void)requestAWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure;
- (void)requestBWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure;
- (void)requestCWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure;


//包装后的方法
- (Promise *)requestAWithArg:(id)arg;
- (Promise *)requestBWithArg:(id)arg;
- (Promise *)requestCWithArg:(id)arg;



//XXNetwork.m
//原有的方法
- (void)requestAWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure {
	//doSomething
}
...
//包装后的方法
- (Promise *)requestAWithArg:(id)arg {
    Promise *p = [Promise new];
    [[XXNetwork shared] requestAWithArg:arg success:^(id dataA){
        p.fulfill(dataA);
    } failure:^(NSError *error){
    	p.reject(error);
    }];
    return p;
}
...
复制代码

viewDidLoad中的代码表示的是:发起网络请求A,然后(then)使用返回数据A作为参数发起网络请求B,然后(then)使用返回数据B作为参数发起网络请求C,然后(then)处理dataC。catch则会处理链上产生的错误。你会发现,这段代码阅读下来非常贴近日常的语言习惯,可怕的回调地狱不见了,网络错误有地方做统一处理,太酷了对不对?更重要的是我们只需要做一些简单的改造或包装。

promise是怎么运作的

在感叹promise的优雅之后,我们产生这些疑问:then是什么?catch是什么?value从哪里来的?为什么需要返回一个promise对象?promise对象的fulfill和reject方法是干什么的?

为了方便理解,先将viewDidLoad中的代码缩减一部分,其他不变,然后看一下执行过程

//ViewController.m
- (void)viewDidLoad {
    [[XXNetwork shared] requestAWithArg:arg].then(^id(id value){
        //value 即 dataA
    });
}

//XXNetwork.h
//原有的方法
//异步的网络请求
- (void)requestAWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure;
- (void)requestBWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure;
- (void)requestCWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure;


//包装后的方法
- (Promise *)requestAWithArg:(id)arg;
- (Promise *)requestBWithArg:(id)arg;
- (Promise *)requestCWithArg:(id)arg;



//XXNetwork.m
//原有的方法
- (void)requestAWithArg:(id)arg success:(void (^)(id data))success failure:(void (^)(NSError *error))failure {
	//doSomething
}
...
//包装后的方法
- (Promise *)requestAWithArg:(id)arg {
    Promise *p = [Promise new];
    [[XXNetwork shared] requestAWithArg:arg success:^(id dataA){
        p.fulfill(dataA);
    } failure:^(NSError *error){
    	p.reject(error);
    }];
    return p;
}
...
复制代码

1.在viewDidLoad执行后,执行XXNetwork单例requestAWithArg:方法

2.在requestAWithArg:方法内创建了一个promise对象。调用原有的方法requestAWithArg:success:failure:,如果异步请求成功则 将会 调用promise的fulfill方法,失败则 将会 调用reject方法。返回这个promise对象

3.调用promise的then方法,将成功后的需要执行的block加入到promise中

4.最后当requestAWithArg:success:failure:进入成功回调则调用promise的fulfill方法,使用promise的then方法加入的block会执行。同理当requestAWithArg:success:failure:进入失败回调则调用promise的reject方法,使用promise的catch方法加入的block就会执行

回想一开始我们对promise对象的描述: promise对象表示异步操作的最终完成(或失败)及其结果值。 现在脑海中有一些轮廓正在出现,让我们结合下面图将它梳理清晰:

谈一谈Promise

promise对象始终处于以下3个状态之一:

  • pending初始化状态

  • fulfilled表示操作已经完成

  • rejected表示操作已经失败

当我们创建一个promise对象时,promise处于pending状态;我们可以通过promise的then,catch等方法将成功或失败后需要执行的任务(block)加入到promise的“回调列表”;当异步操作完成后调用promise的fulfill或reject方法,并传递参数;promise的状态从pending转换到fulfilled或rejected,这样的转换是不可逆的,同时会调用之前使用的then或者catch加入到该promise任务(block);then或者catch方法将会返回一个新的promise对象,我们可以对新的promise继续调用then或catch方法,从而形成了链式调用。这就是promise的核心部分。

目前为止仅简单介绍了promise的一部分,不再做更多的使用介绍,通过以下网址可以获取更多的关于promise的规范和使用方法

developer.mozilla.org/en-US/docs/…

promisesaplus.com/

在代码中使用promise之前

Promise并非OC原生提供,使用PromiseKit是一个好的选择,它有丰富可靠的API,你可以在 这里 找到它,具体的使用方法参考其文档。

使用promise让我们远离了回调地狱,但是我们可以思考下一些问题要如何应对:失去了参数的类型信息要如何处理?promise是否有性能问题?能否中止一个promise的链?引入promise的学习成本有多少等等。这些会问题可以在实践中解开,找到适合使用的场景,做好权衡。

实现一个可用promise库

在了解一些规范后我们可以尝试自己实现一个可用的库 ToyPromise ,你会看到then,catch,finally,race,all这些熟悉的API的具体实现。由于一些原因使用上和promise的规范有一些差异,但核心的部分是不变的。 ToyPromise 目前仅是个玩具,欢迎贡献代码让它成长。


以上所述就是小编给大家介绍的《谈一谈Promise》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据结构与算法分析

数据结构与算法分析

韦斯(Mark Allen Weiss) / 机械工业出版社 / 2010-8 / 45.00元

《数据结构与算法分析:C语言描述》曾被评为20世纪顶尖的30部计算机著作之一,作者在数据结构和算法分析方面卓有建树,他的数据结构和算法分析的著作尤其畅销,并受到广泛好评,已被世界500余所大学选作教材。 在《数据结构与算法分析:C语言描述》中,作者精炼并强化了他对算法和数据结构方面创新的处理方法。通过C程序的实现,着重阐述了抽象数据类型的概念,并对算法的效率、性能和运行时间进行了分析。 ......一起来看看 《数据结构与算法分析》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具