From b92ec82d9b590c8d7350c4791e3183aefee6f926 Mon Sep 17 00:00:00 2001 From: Alan Yeh Date: Fri, 5 Aug 2016 14:36:04 +0800 Subject: [PATCH] Add README --- DOC.md | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 136 ++++++++++++++++++++++++++++++--- 2 files changed, 346 insertions(+), 10 deletions(-) create mode 100644 DOC.md diff --git a/DOC.md b/DOC.md new file mode 100644 index 0000000..863cee3 --- /dev/null +++ b/DOC.md @@ -0,0 +1,220 @@ +# Promise +## 状态 +  每个Promise都只会被执行一次,执行完毕后,Promise就会变成成功或失败状态,并且这个状态不会被改变。 + +  一个Promise必须处于以下几个状态之一: + +* AYPromiseStatePending(Pending): 操作正在执行中(等待执行),可以转换到Fulfilled或Rejected状态。 +* AYPromiseStateFulfilled(Fulfilled): 操作执行成功,且状态不可改变 +* AYPromiseStateRejected(Rejected): 操作执行失败,且状态不可改变 + +  有些文章或Promise实现会出现第4种状态Settled,代表操作已结束,可以认为Settled = Fulfilled & Rejected。他本身并不是一种状态,因为非Pending就是Settled,只是为了说的方便而引入Settled这个说法。 + +  Promise处于Pending状态时,其value一定为nil;处于Fulfilled状态时,其value为处理结果,可能为nil;处于Rejected状态时,其value一定为NSError对象,用于描述Promise被拒绝原因。 + +## CommonJS Promise/A +  AYPromise支持标准的CommonJS Promise/A语法。由于语言的特殊,对其中部份Api进行小量改造。 +### 构造函数 +  由于AYPromise使用链式调用语法,抛弃OC的传统构造函数,使用C构造AYPromise对象更有利于书写方便。AYPromise有以下构造函数: + +#### `AYPromise *AYPromiseWithResolve(void (^)(AYResolve resolve))` + +```objective-c +AYPromise *promise = AYPromiseWithResolve(^(AYResolve resolve){ + resolve(@"aaa"); +}); +``` +  使用AYPromiseWithResolve创建Promise后,会立刻开始在主线程执行block。AYPromiseWithResolve提供了一个回调AYResolve,使用`resolve(value)`返回结果。结果的类型不一样,会导至Promise的行为不一样,返回值可以为以下类型: + +- Promise,当前的Promise对象被抛弃,并将当前Promise链之后的Promise被托管至返回的Promise对象 +- NSError,当前Promise的状态变更成Rejected状态 +- 其它对象(包括nil),当前Promise的状态变更成Fulfilled + +#### `AYPromise *AYPromiseAsyncWithResolve(void (^)(AYResolve resolve))` + +```objective-c +AYPromise *promise = AYPromiseAsyncWithResolve(^(AYResolve resolve){ + resolve(@"aaa"); +}); +``` +  AYPromiseAysncWithResolve是异步执行的AYPromiseWithResolve。 + +#### `AYPromise *AYPromiseWith(id value)` + +```objective-c +AYPromise *promise = AYPromiseWith(^{ + return @"aaa"; +}); +``` +  AYPromiseWith创建Promise对象,参数value可以为以下类型: + +- block,创建一个Pending状态的Promise并同步执行block +- NSInvocation,创建一个Pending状态的Promise并同步执行invocation +- Promise,直接返回该Promise对象 +- 数组,返回Promise.all封装的Promise +- NSError,返回一个Rejected状态的Promise +- 其它对象(包括nil),返回一个Fulfilled状态的Promise + +#### `AYPromise *AYPromiseAsyncWith(id value)` + +```objective-c +AYPromise *promise = AYPromiseAsyncWith(^{ + //异步执行 + return [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:@"https://www.baidu.com"] encoding:NSUTF8StringEncoding error:nil]; +}); +``` +  AYPromiseAsyncWith创建异步执行的Promise +### then +  then语法用于处理正确(Fulfilled)的Promise结果。在CommonJS Promise/A的语法中,then应该支持2个回调函数,第一个回调函数处理Fulfilled的结果,第二个回调函数处理Rejected的结果。为了减少其书写上的复杂性,AYPromise的then语法仅支持1个block,用于处理正确(Fulfilled)结果。 + +```objective-c +promise.then(^(NSString *result){ + return [result stringByAppendingString:@"bbb"]; +}); +``` + +  then语法必须接受一个不为空的block类型的参数。then语法中的block支持无参或1个参数。 + +  如果block: + +* 返回普通OC对象(包括nil),当前Promise状态变更成Fulfilled,vlaue被赋值为返回值。 +* 没有返回值,当前Promise状态变更成Fulfilled,value被赋值为nil。 +* 返回NSError对象,当前Promise状态变更成Rejected,value被赋值为NSError对象。 +* 返回Promise对象,当前Promise对象抛弃,当前Promise链之后的Promise被托管至返回的Promise对象。 +* 抛出错误(仅捕捉NSError错误,不捕捉其它对象),当前Promise状态变更成Rejected,value被赋值为NSError对象。 + +### catch +  catch语法用于处理被拒绝(Rejected)的Promise对象的结果。 + +```objective-c +AYPromiseWithResolve:(^(AYResolve resolve){ + NSError *error = ...; + resolve(error); +}).then(^{ + //由于Promise为Rejected,因此then不执行 +}).catch(^(NSError *error){ + //处理Promise为Rejected +}).catch(^(NSError *error){ + //由于当前的catch之前,Rejected的Promise已经被处理了,所以这个catch不执行 +}); +``` +  catch语法必须接受一个不为空的block类型参数。catch语法中的block支持无参或1个NSError参数。catch专门用于处理被拒绝(Rejected)的Promise,then专门处理正确(Fulfilled)的Promise。 + +```objective-c +AYPromiseWithResolve:(^(AYResolve resolve){ + resolve(@"aaa"); +}).then(^{ + NSError *error;//业务产生了一个错误 + @throw error;//直接抛出异常可以被下一个catch捕捉到 + //return error;//直接返回也可以被下一个catch捕捉到 +}).catch(^(NSError *error){ + //可以处理catch之前错误 +}); +``` +### all +  all语法是静态类函数,接受一个Promise数组,并返回一个包装后的Promise对象,称之为A。A的状态改变有两个条件: + +1. 当数组中所有的promise对象变成成功状态(Fulfilled),这个包装后的A才会把自己变成成功状态。A会等待最慢的那个promise对象变成成功态后才把自己变成成功态,并将promise数组的结果封装成它们的结果数据。 +2. 只要其中一个promise对象变成失败状态(Rejected),这个包装后的A就变成失败状态,并且第一个rejected promise传递过来的NSError值会传递给A后面的catch。 + +  因此,all语法可以理解为判断语句『且』,当所有条件都成功时它才成功,当有一个条件失败时,它就是失败。 + +```objective-c +AYPromise *p1 = AYPromiseWith(^{ + return @"aa"; +}); +AYPromise *p2 = AYPromiseWith(^{ + @throw error; +}); +AYPromise *p3 = AYPromiseWith(^{ + return @"cc"; +}); + +AYPromise *pAll = AYPromise.all(@[p1, p2, p3]).then(^(NSArray *result){ + //then语法不会执行,因为p2抛出异常了 +}).catch(^(NSError *error){ + //catch会执行,捕捉到p2所抛出来的错误 +}); + +/************************************************************/ + +AYPromise *p1 = AYPromiseWith(^{ + return @"aa"; +}); +AYPromise *p2 = AYPromiseWith(^{ + return @"bb"; +}); +AYPromise *p3 = AYPromiseWith(^{ + return @"cc"; +}); + +AYPromise *pAll = AYPromise.all(@[p1, p2, p3]).then(^(NSArray *result){ + //then会执行,result = @[@"aa", @"bb", @"cc"]; +}).catch(^(NSError *error){ + //catch不会执行 +}); +``` + +### race +  race语法是静态类函数,接受一个Promise数组,并返回一个包装后的Promise对象,称之为R。R的状态改变有两个条件: + +1. 当数组中所有的promise对象变成失败状态(Rejected),这个包装后的R才会把自己变成失败状态。R会等待最慢的那个promise对象变成失败状态后才把自己变成失败态,并将promise数组所有NSError封装成一个NSError传递给后面的catch。 +2. 只要其中一个promise对象变成成功状态(Fulfilled),这个包装后的R就变成成功状态,并且将第一个fulfilled promise的结果值传给R后面的then。 + +  因此,race语法可以理解为判断语句的『或』,当所有条件都失败时它才失败,当其中一个条件成功时,它成功。 + +```objective-c +AYPromise *p1 = AYPromiseWith(^{ + return @"aa"; +}); +AYPromise *p2 = AYPromiseWith(^{ + @throw error; +}); +AYPromise *p3 = AYPromiseWithResolve:(^(AYResolve resolve){ + resolve(@"cc"); +}); + +AYPromise *pRace = AYPromise.race(@[p1, p2, p3]).then(^(NSString *result){ + //then会执行,result = @"aa" +}).catch(^(NSError *error){ + //catch不会执行,因为p1是成功状态,代表race是成功的 +}); + +/************************************************************/ + +AYPromise *p1 = AYPromiseWith(^{ + @throw error; +}); +AYPromise *p2 = AYPromiseWithResolve:(^(AYResolve resolve){ + resolve(error); +}); +AYPromise *p3 = AYPromiseWith(^{ + return error; +}); + +AYPromise *pRace = AYPromise.race(@[p1, p2, p3]).then(^(NSString *result){ + //then不会执行,因为race中所有的promise都失败了 +}).catch(^(NSError *error){ + //catch会执行,并且NSArray *errors = error.userInfo[AYPromiseInternalErrorsKey]可以获取所有错误 +}); +``` + +### 扩展语法 +  CommonJS Promise/A中的语法较少,考虑到OC实际运用时的便捷性,AYPromise增加了一些实用方法。 +### always +  always语法与then、catch语法类似,与then只处理正确逻辑、catch只处理错误逻辑不同的是,always总是会执行。 +### thenAsync +  thenAsync与then语法使用一致,但是该方法是在`dispatch_get_global_queue(0, 0)`线程中执行,而then方法总是在主线程中执行。 +### thenOn +  thenOn与then语法使用一致,但参数要求用户传递一个指定线程,则block在该线程下执行。 +### thenPromise +  thenPromise是then语法的一个变种,通过回调来返回结果 +### catchAsync +  catchAsync语法与catch语法使用方法一致,但是该方法是在`dispatch_get_global_queue(0, 0)`线程中执行,而catch方法总是在主线程中执行。 +### catchOn +  catchOn与catch语法使用方法一致,但参数要求用户传递一个指定线程,则block在该线程下执行。 +## 其它 + +1. 关于多线程:AYPromise的方法中,如果没有Async、On这类标识,则block会在主线程中执行,否则会在`dispatch_get_global_queue(0, 0)`或用户指定的线程中执行,添加这些方法主要方便用户在各个线程中切换。 + +2. 关于泛型:AYPromise其实不支持泛型。但是为什么又引用泛型,目的是在于当Promise作为函数结果返回时,标识一下当前Promise将会返回什么样的数据,当继续then的时候,可以知道then所获取的结果是一个怎么样的类型。 diff --git a/README.md b/README.md index 4bfed04..15142f4 100644 --- a/README.md +++ b/README.md @@ -5,24 +5,140 @@ [![License](https://img.shields.io/cocoapods/l/AYPromise.svg?style=flat)](http://cocoapods.org/pods/AYPromise) [![Platform](https://img.shields.io/cocoapods/p/AYPromise.svg?style=flat)](http://cocoapods.org/pods/AYPromise) -## Example +## 获取Promise +  使用[CocoaPods](http://cocoapods.org)可以很方便地引入Promise。Podfile添加AYPromise的依赖。 -To run the example project, clone the repo, and run `pod install` from the Example directory first. +```ruby +pod "AYPromise" +``` -## Requirements +## 文档 +  [具体文档](DOC.md)。 -## Installation +## 简介 +### 什么是Promise +  Promise,承诺,在开发中的意思是,我承诺我去做一些事情,但不一定现在就去做,而是在将来满足一些条件之后才执行。Promise刚开始出现在前端开发领域中,主要用来解决JS开发中的异步问题。在使用Promise之前,异步的处理使用最多的就是用回调这种形式,比如: -AYPromise is available through [CocoaPods](http://cocoapods.org). To install -it, simply add the following line to your Podfile: +```javascript +doSomethingAsync(function(result, error){ + if (error){ + ...//处理error + } else { + ...//处理result + } +}) +``` +  在Objective-C中,这类代码也是非常常见的。例如著名的AFNetworking中,访问网络就是使用block回调。 -```ruby -pod "AYPromise" +```objective-c + [client getPath:@"xxx" + parameters:params + success:^(AFHTTPRequestOperation *operation, id responseObject) { + //处理success + } + failure:^(AFHTTPRequestOperation *operation, NSError *error) { + //处理failure + }] ``` -## Author +  这种书写方式,可以很容易解决对异步操作的处理。但是这样的写法,很容易引起回调金字塔的情况。Promise则对异步处理和处理方法都做了规范和抽象,还给了开发者在异步代码中使用return和throw的能力。这也是Promise存在的真正意义。 +### 使用Promise +  来看一个常见的业务场景,获取联系人需要先访问一次服务器(登录或一些必要的操作),然后再访问一次服务器才能真正获取到有效数据,然后再进行一系列的错误处理,代码冗余复杂。 + +```objective-c +- (void)getContactSuccess:(void(^)(NSArray *))success failure:(void(^)(NSError *))failure{ + [client getPath:@"xxx" + parameters:params + success:^(AFHTTPRequestOperation *operation, id responseObject) { + //处理Json序列化 + NSError *error; + NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error]; + if (error) { + failure(error); + return; + } + if ([dic[@"status"] intValue] == 200) { + //需要依赖上一次网络访问的结果,进行下一步访问 + [client getPath:@"yyy" + parameters:params + success:^(AFHTTPRequestOperation *operation, id responseObject) { + //处理Json + //处理业务 + //组装实体 + //.... + success(result); + } + failure:^(AFHTTPRequestOperation *operation, NSError *error) { + //处理网络错误 + failure(error); + }]; + }else{ + //处理错误 + failure(/*返回错误*/); + } + } + failure:^(AFHTTPRequestOperation *operation, NSError *error) { + //处理网络错误 + failure(error); + }]; +} +``` + +  使用Promise改造一下 + +```objective-c +//网络访问与业务分离 +- (AYPromise *)get:(NSString *)getPath withParam:(id)param{ + return AYPromiseWithResolve(^(AYResolve resolve){ + [client getPath:@"xxx" + parameters:param + success:^(AFHTTPRequestOperation *operation, id responseObject) { + resolve(responseObject); + } failure:^(AFHTTPRequestOperation *operation, NSError *error) { + resolve(error); + }]; + }); +} +//Json序列化与业务分离 +- (AYPromise *)parseJson:(id)responseObject{ + return AYPromiseWith(^id{ + NSError *error; + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error]; + if (error) { + return error; + }else{ + return json; + } + }); +} + +- (AYPromise *)getContacts{ + return AYPromiseWith(^{ + //第一次访问网络 + return [self get:@"xxx" withParam:params]; + }).then(^(id responseObject){ + //处理Json序列化 + return [self parseJson:responseObject]; + }).then(^(NSDictionary *json){ + //第二次访问网络 + return [self get:@"yyy" withParam:params]; + }).then(^(id responseObject){ + //再次处理Json序列化 + return [self parseJson:responseObject]; + }).then(^(NSDictionary* result){ + //处理业务正确性 + if ([result[@"status"] intValue] == 200){ + return result; + }else{ + return error;/*构建一个NSError对象返回*/ + } + }).then(^(NSDictionary* result){ + //组装实体 + }); +} +``` -Alan Yeh, alan@yerl.cn +  可以看到,通过Promise的改造,原本层层嵌套代码,变得有序、清晰起来。什么?你问哪里处理错误?放心,通过Promise,可以进行统一的错误处理。 ## License