Skip to content

Commit

Permalink
Add README
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Yeh committed Aug 5, 2016
1 parent 25e1fa0 commit b92ec82
Show file tree
Hide file tree
Showing 2 changed files with 346 additions and 10 deletions.
220 changes: 220 additions & 0 deletions DOC.md
Original file line number Diff line number Diff line change
@@ -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<NSError *> *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所获取的结果是一个怎么样的类型。
136 changes: 126 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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, [email protected]
  可以看到,通过Promise的改造,原本层层嵌套代码,变得有序、清晰起来。什么?你问哪里处理错误?放心,通过Promise,可以进行统一的错误处理。

## License

Expand Down

0 comments on commit b92ec82

Please sign in to comment.