先来看一个开发中常见的嵌套类型的例子:数组
//通常的block回调嵌套
- (void) _normalExample
{
[self _normalBlock1:^(id value) {
[self _normalBlock2:^(id value) {
[self _normalBlock3:^(id value) {
[self _normalBlock4:^(id value) {
[self _normalBlock5:^(id value) {
[self _normalBlock6:^(id value) {
}];
}];
}];
}];
}];
}];
}
复制代码
//正常blocks
- (void) _normalBlock1:(void(^)(id value))block
{
NSLog(@"_normalBlock1");
block(@(1));
}
- (void) _normalBlock2:(void(^)(id value))block
{
block(@"_normalBlock2");
}
- (void) _normalBlock3:(void(^)(id value))block
{
block(@"_normalBlock3");
}
- (void) _normalBlock4:(void(^)(id value))block
{
block(@"_normalBlock4");
}
- (void) _normalBlock5:(void(^)(id value))block
{
block(@"_normalBlock5");
}
- (void) _normalBlock6:(void(^)(id value))block
{
block(@"_normalBlock6");
}
复制代码
有没有被上面的嵌套整蒙?固然开发中嵌套如此多层的也有但很少见,然鹅这样一坨代码写在那里终归不美观(固然你要是以为有层次感,那我也无法,不过话题仍是要继续),很不利于后期的维护和修改。promise
那么有没有一种方式解决呢?下面引入今天的正题Promise
, 先看下用promise
实现的方式:bash
//采用promise
- (void) _promiseExample
{
[[[[[[[FBLPromise do:^id _Nullable{
return [self _promise1];
}] then:^id _Nullable(id _Nullable value) {
return [self _promise2];
}] then:^id _Nullable(id _Nullable value) {
return [self _promise3];
}] then:^id _Nullable(id _Nullable value) {
return [self _promise4];
}] then:^id _Nullable(id _Nullable value) {
return [self _promise5];
}] then:^id _Nullable(id _Nullable value) {
return [self _promise6];
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
复制代码
//promises
- (FBLPromise *) _promise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise1");
}];
}
- (FBLPromise *) _promise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise2");
}];
}
- (FBLPromise *) _promise3
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise3");
}];
}
- (FBLPromise *) _promise4
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise4");
}];
}
- (FBLPromise *) _promise5
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise5");
}];
}
- (FBLPromise *) _promise6
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise6");
}];
}//promises
- (FBLPromise *) _promise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise1");
}];
}
- (FBLPromise *) _promise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise2");
}];
}
- (FBLPromise *) _promise3
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise3");
}];
}
- (FBLPromise *) _promise4
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise4");
}];
}
- (FBLPromise *) _promise5
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise5");
}];
}
- (FBLPromise *) _promise6
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise6");
}];
}
复制代码
在调用层面是否是感受很舒服?并且调用的逻辑语义也显而易见,若是你以为很爽,那么就接下来一块儿聊聊promise
的使用方法和一些浅显的逻辑原理,若是你以为看着也不舒服,那就继续用Block套block呗。网络
如图为PromisesObjC
的框架结构,除去FBLPromise
、FBLPromiseError
以及头文件,这些都是不一样情形下的实现类,下面咱们先从经常使用的几个入手,熟悉一下如何去使用promise
框架
如字面意思,这个特别适合一件事作完再作另外一件事的逻辑,如A,B两个任务,B依赖于A任务的完成结果,设置一个场景,任务B依赖任务A返回的一个值,进行求和,而后输出求和结果异步
- (void) _thenExample
{
[[[FBLPromise do:^id _Nullable{
return [self _thenPromise1];
}] then:^id _Nullable(id _Nullable value) {
return [self _thenPromise2:[value intValue]];
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"%@",value);
return nil;
}];
}
- (FBLPromise *) _thenPromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@(1));
}];
}
- (FBLPromise *) _thenPromise2:(int)input
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
int sum = input + 10;
fulfill(@(sum));
}];
}
复制代码
do
暂且无论,咱们能够进入then
方法查看源码,它会返回一个FBLPromise
的对象,内部会调用[self chainOnQueue:queue chainedFulfill:work chainedReject:nil]
核心方法,至于这个核心方法中的逻辑咱们稍后讲解原理的时候再说,能够看到正是由于此方法返回的FBLPromise
,才能实现这种链式调用的形式,固然,你能够写成点调用的形式,以下:async
[FBLPromise do:^id _Nullable{
return [self _thenPromise1];
}].then(^ id (id value){
return [self _thenPromise2:[value intValue]];
}).then(^ id (id value){
NSLog(@"%@",value);
return nil;
});
复制代码
但这种写法then
中的参数不会自动补全,写起来比较别扭,因此,下面的例子我都以[]的形式书写,毕竟是OC
的特有结构ui
总结:
then
可用做一个任务的执行依赖另外一个任务完成的结果的场景,开发中常见的是对网络图片加载完成后对图片的特殊处理,如模糊,剪切等,或者网络请求数据完成后对数据的处理等状况,这里再也不赘述,有兴趣的可拿本身项目来练练。spa
Wait until all of the given promises are fulfilled.If one of the given promises is rejected, then the returned promise is rejected with same error.
源码文档中的解释如上:当全部的promise
执行完成才执行另外一个任务,只要有一个promise
执行错误,则这个任务队列直接结束,并返回此错误。此外,执行完成中若是返回值是NSError
的,也按照执行错误处理。简而言之,只要全部的promise
都执行了fulfill()
且fulfill()
中的参数不能为NSError
类型,才算全部的任务执行成功。线程
这种情形在开发中很是常见,特别是在多个任务的情形,并且这些待执行的任务顺序不分前后,可是在then
中的结果返回数组中的元素,通过验证是和all
中数组的顺序是一致的
假设一种场景,C任务在A,B任务都执行成功的时候才执行
/******************** all的使用 *********************/
- (void) _allExample
{
NSArray *promises = @[[self _allPromise1],[self _allPromise2]];
[[[[FBLPromise all:promises] then:^id _Nullable(NSArray * _Nullable value) {
NSLog(@"%@",value);
return [self _allPromise3];
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"%@",value);
return nil;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _allPromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
fulfill(@"_allPromise1 执行完成");
});
}];
}
- (FBLPromise *) _allPromise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_allPromise2 执行完成");
}];
}
- (FBLPromise *) _allPromise3
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_allPromise3 执行完成");
}];
}
复制代码
输出结果以下:
能够看到
[FBLPromise all:promises] then:^id _Nullable(NSArray * _Nullable value) {
NSLog(@"%@",value);
return [self _allPromise3];
}]
复制代码
promises
任务数组的顺序和返回值value
的顺序保持一致性
若是上述两个任务有一个失败,则会进入catch
中,这个留到后面讲解
总结:all只有当给定的全部待执行的任务所有都成功执行,且返回值不能为
NSError
类型,才能继续接下来的任务执行,不然以第一个执行出错的任务的错误做为总体的错误返回,进入到catch
中。
此方法是用来捕捉错误的
/******************** catch的使用 *********************/
- (void) _catchExample
{
[[FBLPromise do:^id _Nullable{
return [self _catchPromise];
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _catchPromise
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
NSError *error = [NSError errorWithDomain:@"catch.example" code:0 userInfo:@{}];
reject(error);
// fulfill(error);
}];
}
复制代码
上述代码便可说明,这里再也不详细解释
A block that always executes, no matter if the receiver is rejected or fulfilled.
复制代码
源码中的注释是,不管接收者是执行成功仍是失败,都会执行此block块
- (void) _alwaysExample
{
[[[[FBLPromise do:^id _Nullable{
return [self _alwaysPromise1];
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"%@",value);
return [self _alwaysPromise2];
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}] always:^{
NSLog(@"老是会执行");
}];
}
- (FBLPromise *) _alwaysPromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
NSError *error = [NSError errorWithDomain:@"catch.example" code:0 userInfo:@{}];
fulfill(error);
}];
}
- (FBLPromise *) _alwaysPromise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_alwaysPromise2");
}];
}
复制代码
输出结果以下:
总结: always是必定会执行的block块,不管前面的执行结果是成功仍是失败
能够理解为
all
的对立面,只有当全部的任务执行都错误后才执行catch
,且捕捉到的是最后一次返回的错误,不然只要有一个任务执行成功,则执行成功。
/******************** any的使用 *********************/
- (void) _anyExample
{
[[[[FBLPromise any:@[[self _anyPromise1],[self _anyPromise2]]] then:^id _Nullable(NSArray * _Nullable value) {
return [self _anyPromise3];
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"%@",value);
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _anyPromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_anyPromise1");
// NSError *error = [NSError errorWithDomain:@"any.example" code:1 userInfo:@{}];
// reject(error);
}];
}
- (FBLPromise *) _anyPromise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
NSError *error = [NSError errorWithDomain:@"any.example" code:0 userInfo:@{}];
reject(error);
}];
}
- (FBLPromise *) _anyPromise3
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_anyPromise3");
}];
}
复制代码
通过验证会发现,只有当_anyPromise1
_anyPromise2
全都执行reject
的时候,才会执行catch
,而不会执行第一个then
中的_anyPromise3
,不然必定会执行第一个then
块里的内容,此外要注意的是,any
中给定的promises
数组必须所有执行完毕,才会继续决定执行then
或者reject
.
此方法建立一个
pending
状态的promise
,且带异步执行的block
块,适合用来处理异步任务回来后的状况,好比网络请求或者耗时操做回来后要作某些事,就可使用此方法,此方法block
块中的FBLPromiseAsyncWorkBlock
含有FBLPromiseFulfillBlock
和FBLPromiseRejectBlock
,能够用来处理异步任务回来后决定是否须要继续执行下一步任务,上面的例子中都用到了此种方式,能够自行测验,再也不附代码说明
此方法是用来生成阻塞当前线程的
promise
,直到当此promise
执行完毕,相似于系统提供的方法sleep()
,它内部其实使用了信号量实现,此方法在特殊状况下可用,通常使用比较少。
它内部使用了
dispatch_after
实现延迟执行的效果,此法简单,再也不赘述
此方法和
any
效果相似,给定的promises
中只要有一个执行成功,则会执行then
以后的内容,只有所有执行失败,才会执行catch
,它和any
的区别是,race
给定的promises
数组只要有一个promise
执行完成,就会执行then
或者catch
,若是所有的promise
都执行,效果和any
同样
/******************** race的使用 *********************/
- (void) _raceExample
{
[[[FBLPromise race:@[[self _racePromise1],[self _racePromise2]]] then:^id _Nullable(id _Nullable value) {
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _racePromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
// fulfill(@"_racePromise1");
NSError *error = [NSError errorWithDomain:@"race.example" code:2 userInfo:@{}];
reject(error);
}];
}
- (FBLPromise *) _racePromise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
// NSError *error = [NSError errorWithDomain:@"race.example" code:0 userInfo:@{}];
// reject(error);
}];
}
复制代码
能够对不一样的promise
注销fulfill
reject
来验证
用一个新的
promise
去替换掉执行失败的promise
,而不让它执行到catch
中,可谓是偷龙换凤,在某些场景下回用到,好比网络请求回来后,若是失败,咱们不想让它走到catch
中报错,就能够用recover
去执行另外一个promise
/******************** recover的使用 *********************/
- (void) _recoverExample
{
[[[[self _recoverPromise1] recover:^id _Nullable(NSError * _Nonnull error) {
if (error) {
return [self _recoverPromise2];
}
return nil;
}] then:^id _Nullable(id _Nullable value) {
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _recoverPromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
// NSError *error = [NSError errorWithDomain:@"recover.example" code:0 userInfo:@{}];
// reject(error);
fulfill(@"_recoverPromise1");
}];
}
- (FBLPromise *) _recoverPromise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_recoverPromise2");
}];
}
复制代码
上面的代码能够自行测验,会更加明白此方法的效果
它是
promise
的一个实例方法,调用它的是一个promise
对象,reduce
数组是一个值数组,reduce
的做用就是用promise
和后面的值数组进行组合,每两个组合可获得一个promise
对象,而后用新的promise
对象和剩下的值进行组合,最终获得一个promise
对象,在reduce
的block
块中咱们能够拿到一个promise
的返回值和另外一个值,咱们能够对这两个值作本身的逻辑处理,以下例子作的是乘积
/******************** reduce的使用 *********************/
- (void) _reduceExample
{
[[[[self _reducePromise1] reduce:@[@(10), @(20)] combine:^id _Nullable(id _Nullable partial, id _Nonnull next) {
return @([partial intValue] * [next intValue]);
}] then:^id _Nullable(id _Nullable value) {
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _reducePromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@(3));
}];
}
复制代码
设置超时时间,若执行时间大于超时时间,则会报错,进入
catch
块中,不然会进入then
中
/******************** timeout的使用 *********************/
- (void) _timeoutExample
{
[[[[self _timeoutPromise] timeout:2] then:^id _Nullable(id _Nullable value) {
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _timeoutPromise
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
fulfill(@"_timeoutPromise");
});
}];
}
复制代码
验证方法,此方法是实例方法,若是知足验证逻辑,则会进入
then
,value是promise
成功后的回传参数,不然会进入catch
中,error表明的是validate
错误
/******************** validate的使用 *********************/
- (void) _validateExample
{
[[[[self _validatePromise] validate:^BOOL(id _Nullable value) {
return ([value intValue] > 50);
}] then:^id _Nullable(id _Nullable value) {
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _validatePromise
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@(20));
}];
}
复制代码
ps:上面列举了
promise
一些常见的方法,其中then
do
all
catch
的结合使用最为常见,async
更常见于建立promise
对象,掌握这几个基本就够项目中使用了,下面说一说promise
中核心的两个方法
- (void)observeOnQueue:(dispatch_queue_t)queue
fulfill:(FBLPromiseOnFulfillBlock)onFulfill
reject:(FBLPromiseOnRejectBlock)onReject {
NSParameterAssert(queue);
NSParameterAssert(onFulfill);
NSParameterAssert(onReject);
@synchronized(self) {
switch (_state) {
case FBLPromiseStatePending: {
if (!_observers) {
_observers = [[NSMutableArray alloc] init];
}
[_observers addObject:^(FBLPromiseState state, id __nullable resolution) {
dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
switch (state) {
case FBLPromiseStatePending:
break;
case FBLPromiseStateFulfilled:
onFulfill(resolution);
break;
case FBLPromiseStateRejected:
onReject(resolution);
break;
}
});
}];
break;
}
case FBLPromiseStateFulfilled: {
dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
onFulfill(self->_value);
});
break;
}
case FBLPromiseStateRejected: {
dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
onReject(self->_error);
});
break;
}
}
}
}
复制代码
此方法首先判断当前
promise
的state
,若是是FBLPromiseStateFulfilled
,直接执行onFulfill
回调,将value
值回调出去。若是是FBLPromiseStateRejected
,直接执行onReject
回调,将error
值回调出去。不然会检查_observers
是否为空,为空则建立一个观察者数组,此数组中存放的对象是FBLPromiseObserver
,它其实是一个block
块typedef void (^FBLPromiseObserver)(FBLPromiseState state, id __nullable resolution);
是一个含有state
和resolution
参数的无返回值block
,此数组中的block
对象会延迟到最终的fulfill:
和reject:
方法中执行回调,在回到中根据state
值,肯定执行下一步操做,将对应的值返回出去。
- (FBLPromise *)chainOnQueue:(dispatch_queue_t)queue
chainedFulfill:(FBLPromiseChainedFulfillBlock)chainedFulfill
chainedReject:(FBLPromiseChainedRejectBlock)chainedReject {
NSParameterAssert(queue);
FBLPromise *promise = [[FBLPromise alloc] initPending];
__auto_type resolver = ^(id __nullable value) {
if ([value isKindOfClass:[FBLPromise class]]) {
[(FBLPromise *)value observeOnQueue:queue
fulfill:^(id __nullable value) {
[promise fulfill:value];
}
reject:^(NSError *error) {
[promise reject:error];
}];
} else {
[promise fulfill:value];
}
};
[self observeOnQueue:queue
fulfill:^(id __nullable value) {
value = chainedFulfill ? chainedFulfill(value) : value;
resolver(value);
}
reject:^(NSError *error) {
id value = chainedReject ? chainedReject(error) : error;
resolver(value);
}];
return promise;
}
复制代码
这个方法的核心要点,也在observeOnQueue:
上,方法中首先定义了一个pending
状态的promise
对象和resolver
回调,而后自身执行的是observeOnQueue:
方法,在回调里,去执行resolver
,那么resolver
中作了什么呢?很简单,根据回传过来的值判断是不是promise
类型,若是是,则对此promise
添加观察者,不然直接执行fulfill
方法,我的认为这里能够细化判断是不是NSError
类型,从而更精确的执行reject:
或者fulfill:
,不过fulfill:
接收参数自己就是id
类型,且内部也作了NSError
的过滤,直接执行也是能够的
只要理解了这两个方法,那么promise
的核心要点也就理解了,最后作一个简单总结:
总结:简而言之,
promise
的实现核心归于对block
块的延迟处理,结合GCD
以及链式调用的思想,从而能够用优雅简洁的方式来处理异步任务,若是想更深入地理解,能够打断点一步步调试