内容简介:本文将会向你简单介绍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对象始终处于以下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/…
在代码中使用promise之前
Promise并非OC原生提供,使用PromiseKit是一个好的选择,它有丰富可靠的API,你可以在 这里 找到它,具体的使用方法参考其文档。
使用promise让我们远离了回调地狱,但是我们可以思考下一些问题要如何应对:失去了参数的类型信息要如何处理?promise是否有性能问题?能否中止一个promise的链?引入promise的学习成本有多少等等。这些会问题可以在实践中解开,找到适合使用的场景,做好权衡。
实现一个可用promise库
在了解一些规范后我们可以尝试自己实现一个可用的库 ToyPromise ,你会看到then,catch,finally,race,all这些熟悉的API的具体实现。由于一些原因使用上和promise的规范有一些差异,但核心的部分是不变的。 ToyPromise 目前仅是个玩具,欢迎贡献代码让它成长。
以上所述就是小编给大家介绍的《谈一谈Promise》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。