一、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简介
-
概念
- Promise是可写的单赋值容器,用于设置future的值,future是值,promise是设定值的函数——本质上是异步函数(promise)的返回值(future)。具体可参考维基百科的文章:Promise和Future。
- Promise可以链接多个异步任务,实现链式编程。它在前端领域应用广泛,甚至已经有了Promises/A+规范。
- 个人理解,在iOS中,Promise就是block的一个一次性执行容器。
-
状态迁移
一个promise必须处于三种状态之一: 请求态(pending), 完成态(fulfilled),拒绝态(rejected)- 当promise处于请求状态(pending)时,可以转为fulfilled或者rejected状态
- 当promise处于完成态(fulfilled)时,promise不能转为任何其他状态,必须有一个值,且此值不能改变。
- 当promise处于拒绝态(rejected)时,promise不能转为任何其他状态,必须有一个原因(reason),且此原因不能改变。
3、iOS中最受欢迎的Promise开源库
-
PromiseKit
- PromiseKit是Max Howell(Homebrew的作者,因为没有写出反转二叉树被Google拒绝😓)的大作,应该是iOS中引用最多的promise实现库,使用Swift实现,也提供了OC的版本(桥接引用Swift的基础实现)。
-
Promises
- Promises是Google在18年上半年开源的项目,同样实现了promise的功能,使用OC实现,也提供了Swift的版本(桥接引用OC的基础实现)。
二、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;
- 介绍
创建一个pending的promise,并且异步执行work block - 实现
调用initPending生成promise,直接调用dispatch_group_async函数在queue中执行work block,work block的第一个参数考虑兼容value是promise的case
2. do
typedef id __nullable (^FBLPromiseDoWorkBlock)(void);
+ (instancetype)onQueue:(dispatch_queue_t)queue do:(FBLPromiseDoWorkBlock)work;
- 介绍
创建一个pending的promise,并且异步执行work block。可以看做async的简版 - 实现
同async类似,只不过通过直接调用work block来获取value
3. then
typedef id __nullable (^FBLPromiseThenWorkBlock)(Value __nullable value);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue then:(FBLPromiseThenWorkBlock)work;
- 介绍
then可以看做简版的chainOnQueue: chainedFulfill: chainedReject:,then对应的是chainedFulfill:。如果前面被监听的promise 状态state是fulfilled,才会调用then对应的的FBLPromiseThenWorkBlock,即promise通过决议时才被调用。如果是rejected,则不会走then对应的block。then可以理解成用于捕获前面异步任务的成功fulfilled value - 实现
直接调用chainOnQueue: chainedFulfill: chainedReject:,chainedFulfill参数传入then work block
4. catch
typedef void (^FBLPromiseCatchWorkBlock)(NSError *error);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue catch:(FBLPromiseCatchWorkBlock)work;
- 介绍
catch和then类似,只不过是监听的promise的状态state是rejected时才会调用。 - 实现
直接调用chainOnQueue: chainedFulfill: chainedReject:,chainedReject参数传入catch work block
5. all
+ (FBLPromise<NSArray *> *)onQueue:(dispatch_queue_t)queue all:(NSArray *)promises;
- 介绍
传入一个promise数组,All会等待所有的promise 状态变为fulfilled,然后按传入promise数组的顺序返回一个value的数组。如果任何一个promise被rejected,则结果promise立刻被rejected。 - 实现
通过async方法生成一个结果promise,然后检查传入的数组参数,将其转变真正的promise数组,遍历此数组,调用[promise observeOnQueue:fulfill:reject]监听每个promise的状态变化,直至所有的promise状态变为fulfilled或者遇到第一个promise状态变为rejected,直接reject 结果promise。
6. always
typedef void (^FBLPromiseAlwaysWorkBlock)(void);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue always:(FBLPromiseAlwaysWorkBlock)work;
- 介绍
无论监听的promise的状态变为fulfilled还是rejected,always block总会执行 - 实现
直接调用[self chainOnQueue:chainedFulfill:chainedReject:],在返回之前调用always work block。
7. any
+ (FBLPromise<NSArray *> *)any:(NSArray *)promises;
- 介绍
传入一个promise数组,Any会等待所有的promise 状态变为fulfilled或者rejected,只要有一个prmise状态完成,这结果promise状态会被决议成fulfilled,当所有的promise状态都变为rejected时,结果promise状态也变为rejected,error信息同状态最后变为rejected的promise的error相同。 - 实现
同all类似,只不过遍历监听promise时,当promise被reject时需要检查是否有一个promise是fulfilled。
8.await
FOUNDATION_EXTERN id __nullable FBLPromiseAwait(FBLPromise *promise, NSError **error);
- 介绍
等待promise被resloved,会阻塞当前线程。 - 实现
使用信号量机制dispatch_semaphore_t实现,利用锁来等待promise被resloved。
注意点:使用dispatch_once只生成一个Await queue。
9. delay
- (FBLPromise *)onQueue:(dispatch_queue_t)queue delay:(NSTimeInterval)interval;
- 介绍
创建一个pending的promise,在delay的时间后,状态变为fulfilled,value同self相同,如果self变为rejected,则忽略delay,直接rejected - 实现
调用self的监听方法,在fulfill的回调中,调用disptch_after
10. race
+ (instancetype)race:(NSArray *)promised;
- 介绍
传入一个promise数组,等待任意一个promise变为fulfilled,如果任意一个promise变为rejected,则结果promise变为rejected。 - 实现
使用async实例化一个结果promise,遍历传入的promise数组,对每个promise进行监听,因为promise的状态特性,第一个promise变为fulfilled或者rejected后,结果promise和它保持一致,且以后的状态不再变化。
11. recover
typedef id __nullable (^FBLPromiseRecoverWorkBlock)(NSError *error);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue recover:(FBLPromiseRecoverWorkBlock)recovery;
- 介绍
当receiver变为rejected时,返回一个新的pending promise,代替rejected的promise - 实现
调用[self chainOnQueue:chainedFulfill:chainedReject:],在chainedReject block中返回recover work block的结果。
12. reduce
typedef id __nullable (^FBLPromiseReducerBlock)(Value __nullable partial, id next);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue reduce:(NSArray *)items combine:(FBLPromiseReducerBlock)reducer;
- 介绍
基于当前的promise和items数据源,实现reduce功能,当前promise的值为初值,最终返回的promise是数组最后一个Item转换的promise。 - 实现
调用[promise chainOnQueue:chainedFulfill:chainedReject:]实现
13. retry
typedef id __nullable (^FBLPromiseRetryWorkBlock)(void);
typedef BOOL (^FBLPromiseRetryPredicateBlock)(NSInteger, NSError *);
+ (FBLPromise *)onQueue:(dispatch_queue_t)queue retry:(FBLPromiseRetryWorkBlock)work;
- 介绍
创建一个pending的promise,以work的返回值作为value,变为fulfilled,如果变为rejected,则retry,在所有的retry次数完成后变为rejected。 - 实现
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;
- 介绍
等待promise直到timeout。返回一个pending promise,它或者以receiver相同的resolution变为fulfilled或者以超时类型的error变为rejected - 实现
监听receiver promise的状态,同时调用dispatch_after 直接reject结果promise。
15. validate
typedef BOOL (^FBLPromiseValidateWorkBlock)(Value __nullable value);
- (FBLPromise *)onQueue:(dispatch_queue_t)queue validate:(FBLPromiseValidateWorkBlock)predicate;
- 介绍
验证promise fulfilled后的value,如果不能通过验证,则reject此promise - 实现
创建一个pending的结果promise,监听当前promise的状态,当promise变为fulfilled时,调用predicate校验值,不通过的话,reject掉结果promise。
三、总结
回到最初遇到的问题,使用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的怀抱吧!
网友评论