PromisesObjC 使用与源码解析

3,757 阅读11分钟

思维导图

先来看一个开发中常见的嵌套类型的例子:

//一般的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
- (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呗。

如何用Promise

整体结构

如图为PromisesObjC的框架结构,除去FBLPromiseFBLPromiseError以及头文件,这些都是不同情形下的实现类,下面我们先从常用的几个入手,熟悉一下如何去使用promise

1. then

如字面意思,这个特别适合一件事做完再做另一件事的逻辑,如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,才能实现这种链式调用的形式,当然,你可以写成点调用的形式,如下:

[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的特有结构

总结:then可用作一个任务的执行依赖另一个任务完成的结果的场景,开发中常见的是对网络图片加载完成后对图片的特殊处理,如模糊,剪切等,或者网络请求数据完成后对数据的处理等情况,这里不再赘述,有兴趣的可拿自己项目来练练。

2. all

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中。

3. 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);
    }];
}

上述代码即可说明,这里不再详细解释

4. always

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块,无论前面的执行结果是成功还是失败

5. any

可以理解为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.

6. async

此方法创建一个pending状态的promise,且带异步执行的block块,适合用来处理异步任务回来后的情况,比如网络请求或者耗时操作回来后要做某些事,就可以使用此方法,此方法block块中的FBLPromiseAsyncWorkBlock含有FBLPromiseFulfillBlockFBLPromiseRejectBlock,可以用来处理异步任务回来后决定是否需要继续执行下一步任务,上面的例子中都用到了此种方式,可以自行测验,不再附代码说明

7. await

此方法是用来生成阻塞当前线程的promise,直到当此promise执行完毕,类似于系统提供的方法sleep(),它内部其实使用了信号量实现,此方法在特殊情况下可用,一般使用比较少。

8. delay:

它内部使用了dispatch_after实现延迟执行的效果,此法简单,不再赘述

9. race:

此方法和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来验证

10. recover

用一个新的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");
    }];
}

上面的代码可以自行测验,会更加明白此方法的效果

11. reduce:

它是promise的一个实例方法,调用它的是一个promise对象,reduce数组是一个值数组,reduce的作用就是用promise和后面的值数组进行组合,每两个组合可得到一个promise对象,然后用新的promise对象和剩下的值进行组合,最终得到一个promise对象,在reduceblock块中我们可以拿到一个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));
    }];
}

12. timeout:

设置超时时间,若执行时间大于超时时间,则会报错,进入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");
        });
    }];
}

13. validate:

验证方法,此方法是实例方法,如果满足验证逻辑,则会进入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中核心的两个方法

observeOnQueue:fulfill:reject

- (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;
      }
    }
  }
}

此方法首先判断当前promisestate,如果是FBLPromiseStateFulfilled,直接执行onFulfill回调,将value值回调出去。如果是FBLPromiseStateRejected,直接执行onReject回调,将error值回调出去。否则会检查_observers是否为空,为空则创建一个观察者数组,此数组中存放的对象是FBLPromiseObserver,它实际上是一个blocktypedef void (^FBLPromiseObserver)(FBLPromiseState state, id __nullable resolution); 是一个含有stateresolution参数的无返回值block,此数组中的block对象会延迟到最终的fulfill:reject:方法中执行回调,在回到中根据state值,确定执行下一步操作,将对应的值返回出去。

chainOnQueue:chainedFulfill:chainedReject:

- (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以及链式调用的思想,从而可以用优雅简洁的方式来处理异步任务,如果想更深刻地理解,可以打断点一步步调试