@xifenglang-33250
2019-01-22T11:34:42.000000Z
字数 27814
阅读 2186
源码学习
Promise旨在链接多个异步任务,实现链式or响应式编程,能有效解决回调地狱CallBack Hell
,在JavaScript应用已经比较常见,概念也趋于成熟,核心概念可了解维基文章:Promise和Future,另外一篇JavaScript Promise A+中文翻译。Google Promise的开发者之一shoumikhin
提到一个概念a promise (as an asynchronous provider) and a future (as an asynchronous return object)
,我理解为promise
是处理异步任务的容器,外部将异步任务放在promise
执行,future
是异步任务的返回值(NSError或其他类型对象)。promise
不改变future
的值,只会根据future
进行鉴权改变promise
自身的状态state,已经bind操作(_value or _error)。promise
的状态有3种,Pending待决议、待处理,Fulfilled已处理且已通过决议,future合法,Rejected已处理但未通过决议,future非法。promise
虽然是基于GCD
实现异步任务容器,但是其内部的成员变量都通过@synchronized(self)
加锁实现线程安全。promise
的三种状态,Pending只能转变成Fulfilled或者Rejected,promise
的状态变成Fulfilled或者Rejected后,其状态state、value和error都不会被再次更改。可以理解Promise
是一次性的异步任务容器。
/** All states a promise can be in. */
typedef NS_ENUM(NSInteger, FBLPromiseState) {
FBLPromiseStatePending = 0,
FBLPromiseStateFulfilled,
FBLPromiseStateRejected,
};
initPending
实例化一个Pending的promise,通过fulfill:
传入future,promise鉴权后更新自己的state,如果future是NSError类型,就会执行reject:
否决,状态改成Rejected,如果是非NSError类型值(anyObject or nil),状态变成Fulfilled。而其中的for (FBLPromiseObserver observer in _observers)
又是一个非常关键的代码,其它的observer(可以理解是其它promise的替身,observer是多层嵌套的block,在block底层实现里会调用 [anotherPromise fulfill:value],以此实现promise传递链nesting promises)。
- (instancetype)initPending {
self = [super init];
if (self) {
// 默认行为 _state = FBLPromiseStatePending;
dispatch_group_enter(FBLPromise.dispatchGroup);
}
return self;
}
- (void)fulfill:(nullable id)value {
if ([value isKindOfClass:[NSError class]]) {
[self reject:(NSError *)value];
} else {
@synchronized(self) {
if (_state == FBLPromiseStatePending) {
_state = FBLPromiseStateFulfilled;
_value = value;
// _pendingObjects = nil;
for (FBLPromiseObserver observer in _observers) {
observer(_state, _value);
}
_observers = nil;
dispatch_group_leave(FBLPromise.dispatchGroup);
}
}
}
}
- (void)reject:(NSError *)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);
}
}
}
- (void)dealloc {
if (_state == FBLPromiseStatePending) {
dispatch_group_leave(FBLPromise.dispatchGroup);
}
}
observeOnQueue: fulfill: reject:
的作用是增加监听者,只不过通过block(onFulfill and onReject)实现回调传值。前面说过promise
的状态一旦变成Fulfilled或者Rejected,其状态和值都不会再改变,所以会直接调用onFulfill(self.value);
或者onReject(self.error);
,将其状态和值传递给其它的监听者another promise obj,如果当前的promise还是Pending状态,会添加到当前promise的NSMutableArray<FBLPromiseObserver> *_observers;
监听者数组中,最后在上面的fulfill:
或reject:
中会for循环调用observer(_state, _value);
notify通知各个监听者。
另外一个需要注意的地方就是dispatch_group_enter(FBLPromise.dispatchGroup);
、dispatch_group_leave(FBLPromise.dispatchGroup);
,FBLPromise.dispatchGroup
是个全局静态gorup,promise的异步任务都是放在dispatch_async_group
执行,而前面的enter+leave等效于dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{}
。而Promise的默认队列是主队列,即在主线程执行,调用onQueue
开头的方法可传入自定义队列,以充分发挥多线程的作用。
- (void)observeOnQueue:(dispatch_queue_t)queue
fulfill:(FBLPromiseOnFulfillBlock)onFulfill
reject:(FBLPromiseOnRejectBlock)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 pipeline
promise链,可以实现类似响应式编程的样式。其核心代码在下面的方法中chainOnQueue: chainedFulfill: chainedReject:
,这个方法会返回一个监听当前promose的新promise。在这个方法中首先实例化anotherPromise,通过observeOnQueue: fulfill: reject:
实现对当前promise的监听,在notify通知回调block中调用[promise fulfill:value];
对新的promise进行决议。在这个过程中实现2个promise的链接,以及值传递。
- (FBLPromise *)chainOnQueue:(dispatch_queue_t)queue
chainedFulfill:(FBLPromiseChainedFulfillBlock)chainedFulfill
chainedReject:(FBLPromiseChainedRejectBlock)chainedReject {
FBLPromise *promise = [[FBLPromise alloc] initPending];
[self observeOnQueue:queue
fulfill:^(id __nullable value) {
value = chainedFulfill ? chainedFulfill(value) : value;
[promise fulfill:value];
}
reject:^(NSError *error) {
id value = chainedReject ? chainedReject(error) : error;
[promise fulfill:value];
}];
return promise;
}
FBLPromiseAsyncWorkBlock work,work放在group异步执行, 返回了2个block参数给外部调用者,外部调用fulfill(value)或者reject(error)其本质还是[promise fulfill:value];
即向promise提供future/resolution用于决议鉴权,以此完成一个promise的使用周期。
typedef void (^FBLPromiseFulfillBlock)(Value __nullable value);
typedef void (^FBLPromiseRejectBlock)(NSError *error);
typedef void (^FBLPromiseAsyncWorkBlock)(FBLPromiseFulfillBlock fulfill,
FBLPromiseRejectBlock reject)
@implementation FBLPromise (AsyncAdditions)
+ (instancetype)onQueue:(dispatch_queue_t)queue async:(FBLPromiseAsyncWorkBlock)work {
FBLPromise *promise = [[FBLPromise alloc] initPending];
dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
work(
^(id __nullable value) {
[promise fulfill:value];
},
^(NSError *error) {
[promise reject:error];
});
});
return promise;
}
__unused FBLPromise <NSNumber *>* numberPromise = [FBLPromise onQueue:FBLPromise.defaultDispatchQueue async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
// 通过fulfill或者reject提供future/resolution
fulfill(nil);
reject([NSError errorWithDomain:NSCocoaErrorDomain code:11 userInfo:@{NSURLErrorFailingURLErrorKey:@"failed"}]);
}];
do:
是async:
的精简版,即将fulfill()和reject()2个block整合到一个block中,这个FBLPromiseDoWorkBlock仍是传递future/resolution。核心是[promise fulfill:value];
这个方法会判断future/resolution是不是NSError类型,如果是NSError类型,会调用reject:
方法,不是的情况才会正常走fulfill流程。
+ (instancetype)onQueue:(dispatch_queue_t)queue do:(FBLPromiseDoWorkBlock)work {
FBLPromise *promise = [[FBLPromise alloc] initPending];
dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
id value = work();
[promise fulfill:value];
});
return promise;
}
Promise执行点语法,但是在OC上由于Xcode联想的问题,使用上没有直接调用方法顺手,也许能通过代码块弥补Xcode类型补全的不足。
// 1.
__unused FBLPromise * doPromise = [FBLPromise onQueue:FBLPromise.defaultDispatchQueue do:^id _Nullable{
// 返回resolution
return [NSError errorWithDomain:NSCocoaErrorDomain code:12 userInfo:@{NSURLErrorFailingURLErrorKey:@"do"}];
return successResolution;
}];
// 2.同1
FBLPromise * dispatch_promise(FBLPromiseDoWorkBlock work){
return [FBLPromise onQueue:FBLPromise.defaultDispatchQueue do:work];
}
dispatch_promise(^id _Nullable{
// 返回resolution
return [NSError errorWithDomain:NSCocoaErrorDomain code:12 userInfo:@{NSURLErrorFailingURLErrorKey:@"do"}];
return successResolution;
}).then(^id _Nullable(id _Nullable value) {
return value;
});
用一个future/resolution快速创建一个promise
- (instancetype)initWithResolution:(nullable id)resolution {
self = [super init];
if (self) {
if ([resolution isKindOfClass:[NSError class]]) {
_state = FBLPromiseStateRejected;
_error = (NSError *)resolution;
} else {
_state = FBLPromiseStateFulfilled;
_value = resolution;
}
}
return self;
}
可用一个已知的future,也可以是直接计算返回的值,去创建一个fulfilled或者rejected promise,一般作为promise pipeline的链头。
[FBLPromise resolvedWith:[NSError errorWithDomain:NSCocoaErrorDomain code:13 userInfo:nil]];
[FBLPromise resolvedWith:anURL.absoluteString]
then:
是精简版的chainOnQueue: chainedFulfill: chainedReject:
,then:
对应的是chainedFulfill:
,即fulfill()。如果前面被监听的promise 状态state是fulfilled,才会调用then对应的的FBLPromiseThenWorkBlock,即promise通过决议时才被调用。如果是rejected,则不会走then对应的block。then可以理解成用于捕获前面异步任务的成功fulfilled value
- (FBLPromise *)onQueue:(dispatch_queue_t)queue then:(FBLPromiseThenWorkBlock)work {
return [self chainOnQueue:queue chainedFulfill:work chainedReject:nil];
}
then中需要返回future/resolution,可以是error或者value(非NSError对象)。通过promise pipeline,chainedStringPromise的value会是then返回的future。
FBLPromise <NSString *>* chainedStringPromise = [aFulfilledPromise then:^id _Nullable(NSData * _Nullable value) {
return @"@then return a new resolution";
}];
[[[resolvedPromise then:^id _Nullable(NSString * _Nullable value) {
return @"then pipeline";
}] then:^id _Nullable(id _Nullable value) {
return value;
}] then:^id _Nullable(id _Nullable value) {
return value;
}];
catch: 和 then: 有点对立的意思,then只在前面的future通过决议(fulfilled promise)时才会调用,catch是未通过决议(rejected promise)时才被调用,catch可直接理解成前面异步任务抛出的异常error。
- (FBLPromise *)onQueue:(dispatch_queue_t)queue catch:(FBLPromiseCatchWorkBlock)reject {
return [self chainOnQueue:queue
chainedFulfill:nil
chainedReject:^id(NSError *error) {
reject(error);
return error;
}];
}
虽然catch不返回新future值,返回源码中可发现return error,即error会被传递到后面的promise。所以多个catch嵌套也可以运行。
[[doPromise catch:^(NSError * _Nonnull error) {
NSLog(@"catched an error: %@",error);
}] catch:^(NSError * _Nonnull error) {
NSLog(@"catched second error: %@",error);
}];
doPromise.catch(^(NSError * _Nonnull error){
NSLog(@"catched an error: %@",error);
}).catch(^(NSError * _Nonnull error){
NSLog(@"catched second error: %@",error);
});
主要做了几件事
[[FBLPromise alloc] initWithResolution:obj]
,而且最终promises数组里的promise要么是fulfilled要么是rejected。[promise observeOnQueue: fulfill: reject];
监听每个promise,由于promises数组的promise已经是fulfilled或者rejected,所以监听的fulfill或者reject回调会马上被调用。所以promises数组的元素有多少个,observe回调就会被调用多少次。但是promise是一次性的,状态一旦变成fulfilled或者rejected,就不能再改变状态或者绑定值。所以由all数组转换成的promises数组中有rejected promise,就会调用reject(error);,尽管可能会被多次调用,但是是一次性的原因,只有第一个rejected promise的error会生效,外部只能捕获到第一个error。便利promise的原因,reject可能会被调用多次,fulfill也会被回调多次,但是里面还有层for循环,只有promises数组的promise都是fulfilled状态,才会调用fulfill([promises valueForKey:NSStringFromSelector(@selector(value))]);
,加上一次性的因素,外部的then会捕获跟all数组同值得数组。
+ (FBLPromise<NSArray *> *)onQueue:(dispatch_queue_t)queue all:(NSArray *)allPromises {
NSParameterAssert(queue);
NSParameterAssert(allPromises);
if (allPromises.count == 0) {
return [[FBLPromise alloc] initWithResolution:@[]];
}
NSMutableArray *promises = [allPromises mutableCopy];
return [FBLPromise
onQueue:queue
async:^(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock reject) {
for (NSUInteger i = 0; i < promises.count; ++i) {
id promise = promises[i];
if ([promise isKindOfClass:self]) {
continue;
} else if ([promise isKindOfClass:[NSError class]]) {
reject(promise);
return;
} else {
[promises replaceObjectAtIndex:i
withObject:[[FBLPromise alloc] initWithResolution:promise]];
}
}
for (FBLPromise *promise in promises) {
// fulfill()和reject()会被调用多次,但只有第一次有效(被then或者catch捕获)
[promise observeOnQueue:queue
fulfill:^(id __unused _) {
// Wait until all are fulfilled. 同时也是在这个地方筛选rejected promise
for (FBLPromise *promise in promises) {
if (!promise.isFulfilled) {
return;
}
}
// If called multiple times, only the first one affects the result.
fulfill([promises valueForKey:NSStringFromSelector(@selector(value))]);
}
reject:^(NSError *error) {
reject(error);
}];
}
}];
}
promises的宗旨之一就是为了避免多层嵌套的promise(nesting promises 或者 callback hell回调地狱),比如下面的:
// 1.nesting promises
[[self loadSomething] then:^id _Nullable(NSString * _Nullable somthing) {
return [[self loadAnother] then:^id _Nullable(NSString * _Nullable another) {
return [self doSomethingWith:somthing andAnother:another];
}];
}];
Promise提供了all来规避avoid nesting promises,同时又可以在多线程异步执行多个任务
[[FBLPromise all:@[[self loadSomething],[self loadAnother]]] then:^id _Nullable(NSArray * _Nullable value) {
return [self doSomethingWith:value.firstObject andAnother:value.lastObject];
}];
如果觉得all的可读性不强,换成方法嵌套,一层一层拆成独立的方法,合理的做法是一个方法包含一个then。但是这种做法还是会有多层嵌套,建议试着去简化all
[[self loadSomething] then:^id _Nullable(NSString * _Nullable value) {
return [self loadAnotherWithSomething:value];
}];
- (FBLPromise <NSString *>*)loadSomething {
return FBLPromise.resolved(@"loadSomething");
}
- (FBLPromise <NSString *>*)loadAnother {
return FBLPromise.resolved(@"loadAnother");
}
- (FBLPromise<NSString *> *)loadAnotherWithSomething:(NSString *)something {
return [[self loadAnother] then:^id(NSString *another) {
return [self doSomethingWith:something andAnother:another];
}];
}
- (id)doSomethingWith:(NSString *)sth andAnother:(NSString *)ant {
return [sth stringByAppendingString:ant];
}
无论promise是fulfilled还是rejected,always都会被调用,只是不会带有promise绑定的数据,是个无参block
- (FBLPromise *)onQueue:(dispatch_queue_t)queue always:(FBLPromiseAlwaysWorkBlock)work {
return [self chainOnQueue:queue
chainedFulfill:^id(id value) {
work();
return value;
}
chainedReject:^id(NSError *error) {
work();
return error;
}];
}
regardless of fulfilled or rejected, always invoke
__unused FBLPromise * alwaysPromise = [[[[FBLPromise resolvedWith:@"always"] then:^id _Nullable(NSArray * _Nullable value) {
NSLog(@"then : %@",value);
return nil;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"catch : %@",error);
}] always:^{
NSLog(@"alway invoke !");
}];
[promises replaceObjectAtIndex: withObject:[[FBLPromise alloc] initWithResolution:promise]];
将future/solution数组转成真正的promise数组。[promise observeOnQueue: fulfill: reject:]
,fulfill(FBLPromiseCombineValuesAndErrors(promises));
更改[步骤2]实例的promise的状态(pending -> fulfilled),可以在外部调用then。
// 提取NSArray<FBLPromise *> *promises数组中的promise.value或者promise.error,重新包装成数组返回
static NSArray *FBLPromiseCombineValuesAndErrors(NSArray<FBLPromise *> *promises) {
NSMutableArray *combinedValuesAndErrors = [[NSMutableArray alloc] init];
for (FBLPromise *promise in promises) {
if (promise.isFulfilled) {
[combinedValuesAndErrors addObject:promise.value ?: [NSNull null]];
continue;
}
if (promise.isRejected) {
[combinedValuesAndErrors addObject:promise.error];
continue;
}
assert(!promise.isPending);
};
return combinedValuesAndErrors;
}
+ (FBLPromise<NSArray *> *)onQueue:(dispatch_queue_t)queue any:(NSArray *)anyPromises {
NSMutableArray *promises = [anyPromises mutableCopy];
return [FBLPromise
onQueue:queue
async:^(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock reject) {
for (NSUInteger i = 0; i < promises.count; ++i) {
id promise = promises[i];
if ([promise isKindOfClass:self]) {
continue;
} else {
[promises replaceObjectAtIndex:i
withObject:[[FBLPromise alloc] initWithResolution:promise]];
}
}
for (FBLPromise *promise in promises) {
[promise observeOnQueue:queue
fulfill:^(id __unused _) {
// Wait until all are resolved.
for (FBLPromise *promise in promises) {
if (promise.isPending) {
return;
}
}
// If called multiple times, only the first one affects the result.
fulfill(FBLPromiseCombineValuesAndErrors(promises));
}
reject:^(NSError *error) {
BOOL atLeastOneIsFulfilled = NO;
for (FBLPromise *promise in promises) {
if (promise.isPending) {
return;
}
if (promise.isFulfilled) {
atLeastOneIsFulfilled = YES;
}
}
if (atLeastOneIsFulfilled) {
fulfill(FBLPromiseCombineValuesAndErrors(promises));
} else {
reject(error);
}
}];
}
}];
}
上面提到了,any数组中的future/resolution要么全部非法rejected,能被catch;其它情况都可以调用then获取数组中的future,只不过需要区分error和其它值。any的应用场景非常少
allArr = @[errorResolution];
[[[FBLPromise any:allArr] then:^id _Nullable(NSArray * _Nullable value) {
NSLog(@"any : %@",value);
// 需要解析数组里的数据,区分正常数据和error
if ([value.firstObject isKindOfClass:NSError.class]) {
NSLog(@"dosomthing");
} else if ([value.lastObject isKindOfClass:NSError.class]) {
NSLog(@"dosomthing");
}
return nil;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"any will never be catched : %@",error);
}];
使用信号量机制dispatch_semaphore_t
实现await,利用锁实现等待上一个promise被resolve。
id __nullable FBLPromiseAwait(FBLPromise *promise, NSError **outError) {
assert(promise);
static dispatch_once_t onceToken;
static dispatch_queue_t queue;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("com.google.FBLPromises.Await", DISPATCH_QUEUE_CONCURRENT);
});
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
id __block resolution;
NSError __block *blockError;
[promise chainOnQueue:queue
chainedFulfill:^id(id value) {
resolution = value;
dispatch_semaphore_signal(semaphore);
return value;
}
chainedReject:^id(NSError *error) {
blockError = error;
dispatch_semaphore_signal(semaphore);
return error;
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (outError) {
*outError = blockError;
}
return resolution;
}
FBLPromiseAwait内部是信号量机制,会锁当前的线程,所以FBLPromiseAwait()所在的线程和等待resolve的promise的执行线程尽量不要是同一线程,不然可能造成死锁。
如果FBLPromiseAwait在线程A上等待,FBLPromiseAwait等待的promise刚好在线程A上等待fulfill或者reject,就会导致死锁。promise的默认线程是主线程,所以要特别注意,比如下面的代码就会造成死锁,而且锁主线程。
[FBLPromise onQueue:FBLPromise.defaultDispatchQueue do:^id _Nullable{
NSError * error;
id result = FBLPromiseAwait([self someAsyncRoutine], &error);
return error ?: result;
}];
// ====== Await 仿耗时任务
- (FBLPromise *)someAsyncRoutine {
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
FBLPromise * promise = [FBLPromise pendingPromise];
int64_t const timeToWait = (int64_t)(4 * NSEC_PER_SEC);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeToWait),
queue, ^{
dispatch_async(FBLPromise.defaultDispatchQueue, ^{
[promise fulfill:@"await"];
});
});
return promise;
}
所以尽量确保FBLPromiseAwait
等待的所在线程和promise执行resolve的线程不是同一条,比如下面的用法。FBLPromiseAwait
尽量不要在主线程执行,防止锁主线程
dispatch_queue_t asyncQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
[FBLPromise onQueue:asyncQueue do:^id _Nullable{
NSError * error;
id result = FBLPromiseAwait([self someAsyncRoutine], &error);
return error ?: result;
}];
其核心代码就是下面的dispatch_after
,延迟调用[promise fulfill:value]
,所以外部的then会延迟响应,但是[promise reject:error];
不会延迟调用,所以外部的catch会立刻调用。
- (FBLPromise *)onQueue:(dispatch_queue_t)queue delay:(NSTimeInterval)interval {
FBLPromise *promise = [[FBLPromise alloc] initPending];
[self observeOnQueue:queue
fulfill:^(id __nullable value) {
dispatch_after(dispatch_time(0, (int64_t)(interval * NSEC_PER_SEC)), queue, ^{
[promise fulfill:value];
});
}
reject:^(NSError *error) {
[promise reject:error];
}];
return promise;
}
可以实现一些延迟操作
FBLPromise * delayPromise = [[FBLPromise do:^id _Nullable{
NSLog(@"开始调用%@",NSDate.date);
return @"delay";
}] delay:2.0];
[delayPromise then:^id _Nullable(id _Nullable value) {
NSLog(@"延迟2秒后调用%@",NSDate.date);
return value;
}];
async:里面虽然有2个for循环,功能是相同的,但由于promise是一次性的,所以race:中return的promise是会由数组中最先resolve的promise决定状态。注意:promise可能在多线程实现,最先resolve的不一定是数组第一个。
+ (instancetype)onQueue:(dispatch_queue_t)queue race:(NSArray *)racePromises {
NSArray *promises = [racePromises copy];
return [FBLPromise onQueue:queue
async:^(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock reject) {
for (id promise in promises) {
if (![promise isKindOfClass:self]) {
fulfill(promise);
return;
}
}
// Subscribe all, but only the first one to resolve will change
// the resulting promise's state.
for (FBLPromise *promise in promises) {
[promise observeOnQueue:queue fulfill:fulfill reject:reject];
}
}];
}
有一种说法:多个异步任务是为了容错,比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。
[[FBLPromise race:@[[FBLPromise resolvedWith:@"race1"],[FBLPromise resolvedWith:@"race2"]]] then:^id _Nullable(id _Nullable value) {
NSLog(@"race: %@",value); // race: race1
return value;
}];
增加一个监听当前promise将要被reject的promise,即当前的promise将要被reject否决,会触发return recovery(error);
,将error传给外部,并将recovery返回值继续返回给当前的promise作为future,以此实现纠错。
- (FBLPromise *)onQueue:(dispatch_queue_t)queue recover:(FBLPromiseRecoverWorkBlock)recovery {
return [self chainOnQueue:queue
chainedFulfill:nil
chainedReject:^id(NSError *error) {
return recovery(error);
}];
}
[[[self someErrorPromise] recover:^id _Nullable(NSError * _Nonnull error) {
NSLog(@"handle some error :%@ ",error);
return @"a fulfilled resolution";
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"got a value %@",value);
return value;
}];
基于当前的promise和items数据源,创建一个扁平的promise链,promise <- promiseA <- promiseB <- promiseC ~。在for循环中将上一个promise的solution和当前item传入reducer(value, item)
,外部按照一定的规则生成新的value传给当前的promise。最终返回的promise是数组最后一个Item转换的promise。
- (FBLPromise *)onQueue:(dispatch_queue_t)queue
reduce:(NSArray *)items
combine:(FBLPromiseReducerBlock)reducer {
FBLPromise *promise = self;
for (id item in items) {
promise = [promise chainOnQueue:queue
chainedFulfill:^id(id value) {
return reducer(value, item);
}
chainedReject:nil];
}
return promise;
}
比如将数组拼接成一个扁平的字符串 @"0" + @[@1, @2, @3] -> @"0, 1, 2, 3"
NSArray<NSNumber *> *numbers = @[ @1, @2, @3 ];
FBLPromise * aPromise = [FBLPromise resolvedWith:@"0"];
[[aPromise reduce:numbers combine:^id(NSString *partialString, NSNumber *nextNumber) {
// partialString == 0, return 0,1
// partialString == 0,1, return 0,1,2
// partialString == 0,1,2, return 0,1,2,3
// 相当于转换成4个链接的promise,@0也是第一个
// 0: [FBLPromise resolvedWith:@"0"] value: 0
// 1-3 即数组转成成的promise链,fulfill(1 or 2 or 3)
return [NSString stringWithFormat:@"%@, %@", partialString, nextNumber.stringValue];
}] then:^id(NSString *string) {
// then捕获的就是promises链的最后一个:@3
// Final result = 0, 1, 2, 3
NSLog(@"Final result = %@", string);
return nil;
}];
正常执行retry block一次,如果retry block返回了Error或者rejected promise,则predicate(count, value)条件允许下,延迟interval秒后启动retry重试count次。
promise:如果发生retry,会递归FBLPromiseRetryAttempt,最终调用[promise reject:value];或[promise fulfill:value];
count:retry的次数
predicate:即将发生retry前 (future是error),predicate(count, value)可以再次判断要不要retry,返回false将不发起retry,而是调用[promise reject:value]
interval: 默认延迟1秒发起retry(递归调用FBLPromiseRetryAttempt)
NSInteger const FBLPromiseRetryDefaultAttemptsCount = 1;
NSTimeInterval const FBLPromiseRetryDefaultDelayInterval = 1.0;
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);
}
}
+ (FBLPromise *)onQueue:(dispatch_queue_t)queue
attempts:(NSInteger)count
delay:(NSTimeInterval)interval
condition:(nullable FBLPromiseRetryPredicateBlock)predicate
retry:(FBLPromiseRetryWorkBlock)work {
FBLPromise *promise = [[FBLPromise alloc] initPending];
FBLPromiseRetryAttempt(promise, queue, count, interval, predicate, work);
return promise;
}
如果第一次捕获到了error,默认重复1次,且延迟1秒后执行重复任务
NSURL *url = [NSURL URLWithString:@"https://www.json.org"];
[[[FBLPromise retry:^id _Nullable{
return [self fetchWithURL:url];
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"retry then %@",value);
return nil;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"catched some error: %@",error);
}];
// 自定义retry的队列、次数、延迟时间、条件
[[[FBLPromise onQueue:gQueue attempts:1 delay:1 condition:^BOOL(NSInteger count, NSError * _Nonnull value) {
// condition 返回false直接走reject,被catch捕获,返回true,延迟后执行重复retry
return true;
} retry:^id _Nullable{
return [self fetchWithURL:url];
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"retry then %@",value);
return nil;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"catched some error: %@",error);
}];;
- (FBLPromise <NSArray *> *)fetchWithURL:(NSURL *)url {
return [FBLPromise wrap2ObjectsOrErrorCompletion:^(FBLPromise2ObjectsOrErrorCompletion _Nonnull handler) {
// [[NSURLSession.sharedSession dataTaskWithURL:url completionHandler:handler] resume];
handler(nil, nil, [NSError errorWithDomain:NSCocoaErrorDomain code:100 userInfo:@{NSURLErrorFailingURLErrorKey:@"fetchWithURL Error"}]);
}];
}
可见在等待promise被resolve时启用了一个延迟任务,因为promise是一次性的,所以promise超时就会先触发dispatch_after,并返回一个自定义的error(FBLPromiseErrorDomain and FBLPromiseErrorCodeTimedOut)。
typedef NS_ENUM(NSInteger, FBLPromiseErrorCode) {
/** Promise failed to resolve in time. */
FBLPromiseErrorCodeTimedOut = 1,
/** Validation predicate returned false. */
FBLPromiseErrorCodeValidationFailure = 2,
}
- (FBLPromise *)onQueue:(dispatch_queue_t)queue timeout:(NSTimeInterval)interval {
NSParameterAssert(queue);
FBLPromise *promise = [[FBLPromise alloc] initPending];
[self observeOnQueue:queue
fulfill:^(id __nullable value) {
[promise fulfill:value];
}
reject:^(NSError *error) {
[promise reject:error];
}];
typeof(self) __weak weakPromise = promise;
dispatch_after(dispatch_time(0, (int64_t)(interval * NSEC_PER_SEC)), queue, ^{
NSError *timedOutError = [[NSError alloc] initWithDomain:FBLPromiseErrorDomain
code:FBLPromiseErrorCodeTimedOut
userInfo:nil];
[weakPromise reject:timedOutError];
});
return promise;
}
注意点:需要判断error的code,[error.domain isEqual:FBLPromiseErrorDomain]
,以区分正常的reject error。
// 仿耗时任务
FBLPromise * promise = [[FBLPromise wrap2ObjectsOrErrorCompletion:^(FBLPromise2ObjectsOrErrorCompletion _Nonnull handler) {
dispatch_after(dispatch_time(0, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
handler(nil, nil, [NSError errorWithDomain:NSCocoaErrorDomain code:10010 userInfo:@{NSURLErrorFailingURLErrorKey:@"timeout"}]);
});
}] timeout:5];
[[promise then:^id _Nullable(id _Nullable value) {
NSLog(@"timeout超时%@",value);
return value;
}] catch:^(NSError * _Nonnull error) {
if ([error.domain isEqual:FBLPromiseErrorDomain] || error.code == FBLPromiseErrorCodeTimedOut) {
NSLog(@"timeout超时:%@",error);
} else {
NSLog(@"timeout出错:%@",error);
}
}];
如果promise将被fulfill,会触发FBLPromiseValidateWorkBlock校验,如果predicate校验通过,则继续执行fulfill(value),校验不通过则fulfill(error),这个error是自定义的(FBLPromiseErrorDomain and FBLPromiseErrorCodeValidationFailure)。
typedef NS_ENUM(NSInteger, FBLPromiseErrorCode) {
/** Promise failed to resolve in time. */
FBLPromiseErrorCodeTimedOut = 1,
/** Validation predicate returned false. */
FBLPromiseErrorCodeValidationFailure = 2,
}
- (FBLPromise*)onQueue:(dispatch_queue_t)queue validate:(FBLPromiseValidateWorkBlock)predicate {
NSParameterAssert(queue);
NSParameterAssert(predicate);
FBLPromiseChainedFulfillBlock chainedFulfill = ^id(id value) {
return predicate(value) ? value :
[[NSError alloc] initWithDomain:FBLPromiseErrorDomain
code:FBLPromiseErrorCodeValidationFailure
userInfo:nil];
};
return [self chainOnQueue:queue chainedFulfill:chainedFulfill chainedReject:nil];
}
Validate可以理解校验是否跟预期的value一致
[[[[self getStringAtURL:url] validate:^BOOL(NSString * _Nullable value) {
return [value isEqualToString:@"some str"];
}] then:^id _Nullable(NSString * _Nullable value) {
return [self doSomethingWith:value andAnother:value];
}] catch:^(NSError * _Nonnull error) {
if ([error.domain isEqual:FBLPromiseErrorDomain] || error.code == FBLPromiseErrorCodeValidationFailure) {
NSLog(@"validate 出错1 :%@",error);
} else {
NSLog(@"validate 出错2 :%@",error);
}
}];
1. 外部调用onQueue:wrapCompletion:,传入work
2. 先调用onQueue:async:返回promise,异步里面的block,回传参数fulfill
3. 调用onQueue:wrapCompletion:里面的work block,回传参数:全局block ^(){}
4. 前面回传的global block即供外部调用的handler,调用时回到onQueue:wrapCompletion:,触发fulfill(nil)
5. 调用fulfill(nil)需要回到onQueue:async:看源码,触发[promise fulfill:value(nil)];,以此结束事件链,被外部的then捕获(reject在此事件链中无参与)
wrapCompletion只是wrap之一,其它的wrap跟其核心流程一致,wrap方便给promise做拓展。
+ (instancetype)onQueue:(dispatch_queue_t)queue
wrapCompletion:(void (^)(FBLPromiseCompletion handler))work {
NSParameterAssert(queue);
NSParameterAssert(work);
return [self onQueue:queue
async:^(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock __unused _) {
work(^{
fulfill(nil);
});
}];
}
wrap方便自定义耗时任务的promise,如果网络请求,promise提供了较大的拓展空间
// 耗时任务
[[[FBLPromise wrap2ObjectsOrErrorCompletion:^(FBLPromise2ObjectsOrErrorCompletion _Nonnull handler) {
dispatch_after(dispatch_time(0, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
handler(nil, nil, [NSError errorWithDomain:NSCocoaErrorDomain code:10010 userInfo:@{NSURLErrorFailingURLErrorKey:@"timeout"}]);
});
}]
[[[FBLPromise wrapErrorCompletion:^(FBLPromiseErrorCompletion _Nonnull handler) {
// handler([NSError errorWithDomain:NSCocoaErrorDomain code:101010 userInfo:nil]);
handler(nil);
}] recover:^id _Nullable(NSError * _Nonnull error) {
return @"solution";
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"wrapErrorCompletion value is nil if not recover. %@",value);
return value;
}];
[[[FBLPromise wrapErrorOrObjectCompletion:^(FBLPromiseErrorOrObjectCompletion _Nonnull handler) {
handler([NSError errorWithDomain:NSCocoaErrorDomain code:101010 userInfo:nil], @"dwadwa");
}] recover:^id _Nullable(NSError * _Nonnull error) {
return @"solution";
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"wrapErrorOrObjectCompletion value %@",value);
return value;
}];