iOS Promise学习小结

2019-11-07  本文已影响0人  天赐129

一、Promise概述

1、为什么引入Promise

在日常开发中,异步操作是很常见的情况,比如发起网络请求后,处理网络请求的结果一般都是异步操作,我们一般通过block或者delegate来执行异步操作。但是当有多个异步操作需要嵌套执行时,写出的代码结构就会比较混乱,难以阅读,比较容易出bug。
假设有这样的一种场景,播放加密视频时,需要先通过资源ID获取到视频ID,然后进行解密操作,最后拿解密后的结果去请求视频的播放地址或者视频本身,一般写法,代码如下:

// 仅供演示,未考虑引用循环以及错误处理
[self getVideoIDWithAssetResourceID:@"asset-resource-id-1234" complection:^(NSString *videoID) {
        [self decryptVideoWithVideoID:videoID complection:^(NSString *decryptResult) {
            [self requestVideoDataWithParam:decryptResult complection:^(NSString *videoPlayURLString) {
                [self.player setVideoURLString:videoPlayURLString];
            }];
        }];
    }];

相信大家看到这样的代码,脑袋已经有点开始发懵了吧,这个时候promise就能大显身手了,如果能引入promise,实现同样功能的代码写法就比较优美。至于写法是怎样的呢,容我先买个关子。
所以引入promise是为了解决日常开发中遇到的“回调地狱”问题,使得代码结构更加清晰,易于阅读。

2、Promise简介

3、iOS中最受欢迎的Promise开源库

二、Promises源码分析

相对于Swift,笔者日常开发用OC更多一些,因此重点看了Promises的源码,以下就对它的源码进行下简单解析。

1、核心源码

先看FBLPromise.h,它提供了暴露给业务方的接口。上文提到了promise的状态,promise.h提供了初始化三种状态的promise实例函数以及由pending转为fulfilled或者rejected状态的函数,同时支持指定回调的派发队列的能力。
未用过class修饰property的可以稍微补下课哈。在OC中使用 class 属性修饰符

@property (class) dispatch_queue_t defaultDispatchQueue;

+ (instancetype)pendingPromise;
+ (instancetype)resolvedWith:(nullable id)resolution;
- (void)fulfill:(nullable Value)value;
- (void)reject:(NSError *)error;

再看FBLPromisePrivate.h,提供库内部使用的接口和定义,它提供了一些回调block的定义、对promise状态监听的响应函数以及用于支持链式调用的函数。

typedef void (^FBLPromiseOnFulfillBlock)(Value __nullable value);
typedef void (^FBLPromiseOnRejectBlock)(NSError *error);
typedef id __nullable (^__nullable FBLPromiseChainedFulfillBlock)(Value __nullable value);
typedef id __nullable (^__nullable FBLPromiseChainedRejectBlock)(NSError *error);

- (instancetype)initPending;
- (instancetype)initWithResolution:(nullable id)resolution;
- (void)observeOnQueue:(dispatch_queue_t)queue fulfill:(FBLPromiseOnFulfillBlock)onFulfill reject:(FBLPromiseOnRejectBlock)onReject;
- (FBLPromise *)chainedOnQueue:(dispatch_queue_t)queue chainedFulfill:(FBLPromiseChainedFulfillBlock)chainedFulfill chainedReject:(FBLPromiseChainedRejectBlock)chainedReject;

再看FBLPromise.m,提供上述两个文件的实现。先思考它需要持有哪些对象:状态,value,error,存储block回调的数组,当promise处于pending状态时需要持有的对象(Swift实现中用于promise保活以实现链式调用)

typedef NS_ENUM(NSInteger, FBLPromiseState) {
  FBLPromiseStatePending = 0,
  FBLPromiseStateFulfilled,
  FBLPromiseStateRejected,
};
typedef void (^FBLPromiseObserver)(FBLPromiseState state, id __nullable resolution);

static dispatch_queue_t gFBLPromiseDefaultDispatchQueue;

@implement FBLPromise {
  FBLPromiseState _state;
  NSMutableSet *__nullable _pendingObjects;
  id __nullable _value;
  NSError *__nullable _error;
  NSMutableArray<FBLPromiseObserver> *_observers;
}

+ (void)initialize {
  if(self == [FBLPromise class]) {
    gFBLPromiseDefaultDispatchQueue = dispatch_get_main_queue();
  }
}

/*
为了保证线程安全,在对派发队列或者状态做读写时,需要加锁
*/
+ (dispatch_queue_t)defaultDispatchQueue {
  @synchronized(self) {
    return gFBLPromiseDefaultDispatchQueue;
  }
}

+ (void)setDefaultDispatchQueue:(dispatch_queue_t)queue {
  NSParameterAssert(queue);
  @synchronized(self) {
    gFBLPromiseDefaultDispatchQueue = queue;
  }
}

// 先check value类型,注意加锁,因为是一次性block容器,需要添加状态检查
- (void)fulfill:(nullable id)value {
  if([value isKindOfClass:[NSError class]]) {
    [self reject:(NSError *)value];
  } else {
    @synchronized(self) {
      if (_state == FBLPromiseStatePending) {
        _value = value;
        _state = FBLPromiseStateFulfilled;
        _pendingObjects = nil;
        for (FBLPromiseObserver observer in _observers) {
          observer(_state, _value);
        }
        _observers = nil;
        dispatch_group_leave(FBLPromise.dispatchGroup);
      }
    }
  }
}

// 先check error类型,注意加锁,因为是一次性block容器,需要添加状态检查
- (void)reject:(NSError *)error {
  NSAssert([error isKindOfClass:[NSError class]], @"Invalid error type.");
  if (![error isKindOfClass:[NSError class]]) {
    @throw error;
  }
  @synchronized(self) {
    if (_state == FBLPromiseStatePending) {
      _state = FBLPromiseStateRejected;
      _error = error;
      _pendingObjects = nil;
      for (FBLPromiseObserver *observer in _observers) {
        observer(_state, _error);
      }
      _observers = nil;
      dispatch_group_leave(FBLPromise.dispatchGroup);
    }
  }
}

// 为啥要加group,Testing中有一个FBLWaitForPromisesWithTimeout方法,需要调用dispatch_group_wait方法(等待group中所有block执行完毕,或者在指定时间结束后回调)。
- (instancetype)initPending {
  self = [super init];
  if (self) {
    dispatch_group_enter(FBLPromise.dispatchGroup);
  }
  return self;
}

- (instancetype)initWithResolution:(nullable id)resolution {
  self = [super init];
  if (self) {
    if ([resolution isKindOfClass:[NSError class]]) {
      _state = FBLPromiseStateRejected;
      _error = resolution;
    } else {
      _state = FBLPromiseStateFulfilled;
      _value = resolution;
    }
  }
  return self;
}

// check状态,是否需要leave group
- (void)dealloc {
  if (_state == FBLPromiseStatePending) {
    dispatch_group_leave(FBLPromise.dispatchGroup);
  }
}

// 监听promise状态,调用onFulfill 或者 onReject
- (void)observeOnQueue:(dispatch_queue_t)queue fulfill:(FBLPromiseOnFulfillBlock)onFullfill reject:(FBLPromiseOnRejectBlock)onReject {
  NSParameterAssert(queue);
  NSParameterAssert(onFulfill);
  NSParameterAssert(onReject);
  @synchronized(self) {
    switch(_state) {
      case FBLPromiseStatePending: {
        if (_observers == nil) {
          _observerss = [[NSMutableArray alloc] init];
        }
        FBLPromiseObserver observer = ^(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;
            }
          });
        }
        [_observers addObject:observer];
        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;
    }
  }
}

// 解决链式调用问题, 摊开resolver block
- (FBLPromise *)chainOnQueue:(dispatch_queue_t)queue chainedFulfill:(FBLPromiseChainedFulfillBlock)chainedFulfill chainedReject:(FBLPromiseChainedRejectBlock)chainedReject {
  NSParameterAssert(queue);
  NSParameterAssert(chainedFulfill);
  NSParameterAssert(chainedReject);
  FBLPromise *promise = [[FBLPromise alloc] initPending];
  [self observe:queue onFulfill:^(id __nullable value) {
    value = chainedFulfill ? chainedFulfill(value) : value;
    if ([value isKindOfClass:[FBLPromise class]]) {
      [(FBLPromise *)value observe:queue onFulfill:^(id __nullable value) {
        [promise fulfill:value];
      } onReject:^(NSError *error) {
        [promise reject:error];
      }];
    } else {
      [promise fulfill:value];
    }
  } onReject:^(NSError *error) {
    id value = chainedReject ? chainedReject(value) : value;
    if ([value isKindOfClass:[FBLPromise class]]) {
      [(FBLPromise *)value observe:queue onFulfill:^(id __nullable value) {
        [promise fulfill:value];
      } onReject:^(NSError *error) {
        [promise reject:error];
      }];
    } else {
      [promise fulfill:value];
    }
  }];
return promise;
}

2、支持能力

此处只介绍功能特性以及实现思路,具体实现可以参见源码,使用方法可以参照Tests。

1. async
typedef void (^FBLPromiseFulfillBlock)(Value __nullable value);
typedef void (^FBLPromiseRejectBlock)(NSError *error);
typedef void(^FBLPromiseAsyncWorkBlock)(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock reject);
+ (instancetype)onQueue:(dispatch_queue_t)queue async:(FBLPromiseWorkBlock)work;
2. do
typedef id __nullable (^FBLPromiseDoWorkBlock)(void);
+ (instancetype)onQueue:(dispatch_queue_t)queue do:(FBLPromiseDoWorkBlock)work;
3. then
typedef id __nullable (^FBLPromiseThenWorkBlock)(Value __nullable value);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue then:(FBLPromiseThenWorkBlock)work;
4. catch
typedef void (^FBLPromiseCatchWorkBlock)(NSError *error);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue catch:(FBLPromiseCatchWorkBlock)work;
5. all
+ (FBLPromise<NSArray *> *)onQueue:(dispatch_queue_t)queue all:(NSArray *)promises;
6. always
typedef void (^FBLPromiseAlwaysWorkBlock)(void);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue always:(FBLPromiseAlwaysWorkBlock)work;
7. any
+ (FBLPromise<NSArray *> *)any:(NSArray *)promises;
8.await
FOUNDATION_EXTERN id __nullable FBLPromiseAwait(FBLPromise *promise, NSError **error);
9. delay
- (FBLPromise *)onQueue:(dispatch_queue_t)queue delay:(NSTimeInterval)interval;
10. race
+ (instancetype)race:(NSArray *)promised;
11. recover
typedef id __nullable (^FBLPromiseRecoverWorkBlock)(NSError *error);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue recover:(FBLPromiseRecoverWorkBlock)recovery;
12. reduce
typedef id __nullable (^FBLPromiseReducerBlock)(Value __nullable partial, id next);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue reduce:(NSArray *)items combine:(FBLPromiseReducerBlock)reducer;
13. retry
typedef id __nullable (^FBLPromiseRetryWorkBlock)(void);
typedef BOOL (^FBLPromiseRetryPredicateBlock)(NSInteger, NSError *);
+ (FBLPromise *)onQueue:(dispatch_queue_t)queue retry:(FBLPromiseRetryWorkBlock)work;
static void FBLPromiseRetryAttempt(FBLPromise *promise, dispatch_queue_t queue, NSInteger count, NSTimeInterval interval, FBLPromiseRetryPredicateBlock predicate, FBLPromiseRetryWorkBlock work) {
  __auto_type retrier = ^(id __nullable value) {
    if ([value isKindOfClass:[NSError class]]) {
      if (count <= 0 || (predicate && !predicate(count, value))) {
        [promise reject:value];
      } else {
        dispatch_after(dispatch_time(0, (int64_t)(interval * NSEC_PER_SEC)), queue, ^{
          FBLPromiseRetryAttempt(promise, queue, count - 1, interval, predicate, work);
        });
      }
    } else {
      [promise fulfill:value];
    }
  };
  id value = work();
  if ([value isKindOfClass:[FBLPromise class]]) {
   [(FBLPromise *)value observeOnQueue:queue fulfill:retrier reject:retrier];
  } else {
    retrier(value);
  }
}
14. timeout
- (FBLPromise *)onQueue:(dispatch_queue_t)queue timeout:(NSTimeInterval)interval;
15. validate
typedef BOOL (^FBLPromiseValidateWorkBlock)(Value __nullable value);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue validate:(FBLPromiseValidateWorkBlock)predicate;

三、总结

回到最初遇到的问题,使用Promise以后,代码可能如下:

[[[[[self getVideoIDWithAssetResourceID:@"asset-resource-id-1234"] then:^id(NSString *value){
        return [self decryptVideoWithVideoID:value];
    }] then:^id(NSString *value){
        return [self requestVideoDataWithParam:value];
    }] then:^id(NSString *value) {
        [self.player setVideoURLString:value];
        return value;
    }] catch:^(NSError *error) {
        NSLog(@"play failed!");
    }];

相对开始的写法,结构清晰的多,而且这仅仅应用了then特性,Promise还提供了上述各种特性,灵活的结合使用它们能让你写起代码来更加如鱼得水。
So,尽早摆脱“回调地狱”,投入Promise的怀抱吧!

上一篇 下一篇

猜你喜欢

热点阅读