谈一谈Promise

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

内容简介:本文将会向你简单介绍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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Practical Vim, Second Edition

Practical Vim, Second Edition

Drew Neil / The Pragmatic Bookshelf / 2015-10-31 / USD 29.00

Vim is a fast and efficient text editor that will make you a faster and more efficient developer. It’s available on almost every OS, and if you master the techniques in this book, you’ll never need an......一起来看看 《Practical Vim, Second Edition》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具