[关闭]
@xifenglang-33250 2019-01-22T11:34:42.000000Z 字数 27814 阅读 2161

Google Promises源码学习[1]

源码学习


Promises简介

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是一次性的异步任务容器。

  1. /** All states a promise can be in. */
  2. typedef NS_ENUM(NSInteger, FBLPromiseState) {
  3. FBLPromiseStatePending = 0,
  4. FBLPromiseStateFulfilled,
  5. FBLPromiseStateRejected,
  6. };

Promise核心源码

initPending

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

  1. - (instancetype)initPending {
  2. self = [super init];
  3. if (self) {
  4. // 默认行为 _state = FBLPromiseStatePending;
  5. dispatch_group_enter(FBLPromise.dispatchGroup);
  6. }
  7. return self;
  8. }
  9. - (void)fulfill:(nullable id)value {
  10. if ([value isKindOfClass:[NSError class]]) {
  11. [self reject:(NSError *)value];
  12. } else {
  13. @synchronized(self) {
  14. if (_state == FBLPromiseStatePending) {
  15. _state = FBLPromiseStateFulfilled;
  16. _value = value;
  17. // _pendingObjects = nil;
  18. for (FBLPromiseObserver observer in _observers) {
  19. observer(_state, _value);
  20. }
  21. _observers = nil;
  22. dispatch_group_leave(FBLPromise.dispatchGroup);
  23. }
  24. }
  25. }
  26. }
  27. - (void)reject:(NSError *)error {
  28. @synchronized(self) {
  29. if (_state == FBLPromiseStatePending) {
  30. _state = FBLPromiseStateRejected;
  31. _error = error;
  32. // _pendingObjects = nil;
  33. for (FBLPromiseObserver observer in _observers) {
  34. observer(_state, _error);
  35. }
  36. _observers = nil;
  37. dispatch_group_leave(FBLPromise.dispatchGroup);
  38. }
  39. }
  40. }
  41. - (void)dealloc {
  42. if (_state == FBLPromiseStatePending) {
  43. dispatch_group_leave(FBLPromise.dispatchGroup);
  44. }
  45. }

promise observer [observeOnQueue: fulfill: reject:]

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开头的方法可传入自定义队列,以充分发挥多线程的作用。

  1. - (void)observeOnQueue:(dispatch_queue_t)queue
  2. fulfill:(FBLPromiseOnFulfillBlock)onFulfill
  3. reject:(FBLPromiseOnRejectBlock)onReject {
  4. @synchronized(self) {
  5. switch (_state) {
  6. case FBLPromiseStatePending: {
  7. if (!_observers) {
  8. _observers = [[NSMutableArray alloc] init];
  9. }
  10. [_observers addObject:^(FBLPromiseState state, id __nullable resolution) {
  11. dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
  12. switch (state) {
  13. case FBLPromiseStatePending:
  14. break;
  15. case FBLPromiseStateFulfilled:
  16. onFulfill(resolution);
  17. break;
  18. case FBLPromiseStateRejected:
  19. onReject(resolution);
  20. break;
  21. }
  22. });
  23. }];
  24. break;
  25. }
  26. case FBLPromiseStateFulfilled: {
  27. dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
  28. onFulfill(self->_value);
  29. });
  30. break;
  31. }
  32. case FBLPromiseStateRejected: {
  33. dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
  34. onReject(self->_error);
  35. });
  36. break;
  37. }
  38. }
  39. }
  40. }

promise pipeline [chainOnQueue: chainedFulfill: chainedReject:]

promise pipelinepromise链,可以实现类似响应式编程的样式。其核心代码在下面的方法中chainOnQueue: chainedFulfill: chainedReject:,这个方法会返回一个监听当前promose的新promise。在这个方法中首先实例化anotherPromise,通过observeOnQueue: fulfill: reject:实现对当前promise的监听,在notify通知回调block中调用[promise fulfill:value];对新的promise进行决议。在这个过程中实现2个promise的链接,以及值传递。

  1. - (FBLPromise *)chainOnQueue:(dispatch_queue_t)queue
  2. chainedFulfill:(FBLPromiseChainedFulfillBlock)chainedFulfill
  3. chainedReject:(FBLPromiseChainedRejectBlock)chainedReject {
  4. FBLPromise *promise = [[FBLPromise alloc] initPending];
  5. [self observeOnQueue:queue
  6. fulfill:^(id __nullable value) {
  7. value = chainedFulfill ? chainedFulfill(value) : value;
  8. [promise fulfill:value];
  9. }
  10. reject:^(NSError *error) {
  11. id value = chainedReject ? chainedReject(error) : error;
  12. [promise fulfill:value];
  13. }];
  14. return promise;
  15. }

async:

async:源码

FBLPromiseAsyncWorkBlock work,work放在group异步执行, 返回了2个block参数给外部调用者,外部调用fulfill(value)或者reject(error)其本质还是[promise fulfill:value]; 即向promise提供future/resolution用于决议鉴权,以此完成一个promise的使用周期。

  1. typedef void (^FBLPromiseFulfillBlock)(Value __nullable value);
  2. typedef void (^FBLPromiseRejectBlock)(NSError *error);
  3. typedef void (^FBLPromiseAsyncWorkBlock)(FBLPromiseFulfillBlock fulfill,
  4. FBLPromiseRejectBlock reject)
  1. @implementation FBLPromise (AsyncAdditions)
  2. + (instancetype)onQueue:(dispatch_queue_t)queue async:(FBLPromiseAsyncWorkBlock)work {
  3. FBLPromise *promise = [[FBLPromise alloc] initPending];
  4. dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
  5. work(
  6. ^(id __nullable value) {
  7. [promise fulfill:value];
  8. },
  9. ^(NSError *error) {
  10. [promise reject:error];
  11. });
  12. });
  13. return promise;
  14. }

async:使用

  1. __unused FBLPromise <NSNumber *>* numberPromise = [FBLPromise onQueue:FBLPromise.defaultDispatchQueue async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
  2. // 通过fulfill或者reject提供future/resolution
  3. fulfill(nil);
  4. reject([NSError errorWithDomain:NSCocoaErrorDomain code:11 userInfo:@{NSURLErrorFailingURLErrorKey:@"failed"}]);
  5. }];

do:

do:async:的精简版,即将fulfill()和reject()2个block整合到一个block中,这个FBLPromiseDoWorkBlock仍是传递future/resolution。核心是[promise fulfill:value];这个方法会判断future/resolution是不是NSError类型,如果是NSError类型,会调用reject:方法,不是的情况才会正常走fulfill流程。

do:源码

  1. + (instancetype)onQueue:(dispatch_queue_t)queue do:(FBLPromiseDoWorkBlock)work {
  2. FBLPromise *promise = [[FBLPromise alloc] initPending];
  3. dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
  4. id value = work();
  5. [promise fulfill:value];
  6. });
  7. return promise;
  8. }

do:使用

Promise执行点语法,但是在OC上由于Xcode联想的问题,使用上没有直接调用方法顺手,也许能通过代码块弥补Xcode类型补全的不足。

  1. // 1.
  2. __unused FBLPromise * doPromise = [FBLPromise onQueue:FBLPromise.defaultDispatchQueue do:^id _Nullable{
  3. // 返回resolution
  4. return [NSError errorWithDomain:NSCocoaErrorDomain code:12 userInfo:@{NSURLErrorFailingURLErrorKey:@"do"}];
  5. return successResolution;
  6. }];
  7. // 2.同1
  8. FBLPromise * dispatch_promise(FBLPromiseDoWorkBlock work){
  9. return [FBLPromise onQueue:FBLPromise.defaultDispatchQueue do:work];
  10. }
  11. dispatch_promise(^id _Nullable{
  12. // 返回resolution
  13. return [NSError errorWithDomain:NSCocoaErrorDomain code:12 userInfo:@{NSURLErrorFailingURLErrorKey:@"do"}];
  14. return successResolution;
  15. }).then(^id _Nullable(id _Nullable value) {
  16. return value;
  17. });

resolvedWith:

用一个future/resolution快速创建一个promise

resolvedWith:源码

  1. - (instancetype)initWithResolution:(nullable id)resolution {
  2. self = [super init];
  3. if (self) {
  4. if ([resolution isKindOfClass:[NSError class]]) {
  5. _state = FBLPromiseStateRejected;
  6. _error = (NSError *)resolution;
  7. } else {
  8. _state = FBLPromiseStateFulfilled;
  9. _value = resolution;
  10. }
  11. }
  12. return self;
  13. }

resolvedWith:使用

可用一个已知的future,也可以是直接计算返回的值,去创建一个fulfilled或者rejected promise,一般作为promise pipeline的链头。

  1. [FBLPromise resolvedWith:[NSError errorWithDomain:NSCocoaErrorDomain code:13 userInfo:nil]];
  2. [FBLPromise resolvedWith:anURL.absoluteString]

then:

then:是精简版的chainOnQueue: chainedFulfill: chainedReject:then:对应的是chainedFulfill:,即fulfill()。如果前面被监听的promise 状态state是fulfilled,才会调用then对应的的FBLPromiseThenWorkBlock,即promise通过决议时才被调用。如果是rejected,则不会走then对应的block。then可以理解成用于捕获前面异步任务的成功fulfilled value

then:源码

  1. - (FBLPromise *)onQueue:(dispatch_queue_t)queue then:(FBLPromiseThenWorkBlock)work {
  2. return [self chainOnQueue:queue chainedFulfill:work chainedReject:nil];
  3. }

then:使用

then中需要返回future/resolution,可以是error或者value(非NSError对象)。通过promise pipeline,chainedStringPromise的value会是then返回的future。

  1. FBLPromise <NSString *>* chainedStringPromise = [aFulfilledPromise then:^id _Nullable(NSData * _Nullable value) {
  2. return @"@then return a new resolution";
  3. }];
  4. [[[resolvedPromise then:^id _Nullable(NSString * _Nullable value) {
  5. return @"then pipeline";
  6. }] then:^id _Nullable(id _Nullable value) {
  7. return value;
  8. }] then:^id _Nullable(id _Nullable value) {
  9. return value;
  10. }];

catch:

catch: 和 then: 有点对立的意思,then只在前面的future通过决议(fulfilled promise)时才会调用,catch是未通过决议(rejected promise)时才被调用,catch可直接理解成前面异步任务抛出的异常error。

catch:源码

  1. - (FBLPromise *)onQueue:(dispatch_queue_t)queue catch:(FBLPromiseCatchWorkBlock)reject {
  2. return [self chainOnQueue:queue
  3. chainedFulfill:nil
  4. chainedReject:^id(NSError *error) {
  5. reject(error);
  6. return error;
  7. }];
  8. }

catch:使用

虽然catch不返回新future值,返回源码中可发现return error,即error会被传递到后面的promise。所以多个catch嵌套也可以运行。

  1. [[doPromise catch:^(NSError * _Nonnull error) {
  2. NSLog(@"catched an error: %@",error);
  3. }] catch:^(NSError * _Nonnull error) {
  4. NSLog(@"catched second error: %@",error);
  5. }];
  6. doPromise.catch(^(NSError * _Nonnull error){
  7. NSLog(@"catched an error: %@",error);
  8. }).catch(^(NSError * _Nonnull error){
  9. NSLog(@"catched second error: %@",error);
  10. });

all:

all:源码

主要做了几件事

  1. 通过async相关方法实例一个aPromise返回给外部,通过在内部的for循环中调用
  2. 将外部传进的数组allPromises转换成真正的promise对象数组promises,[[FBLPromise alloc] initWithResolution:obj],而且最终promises数组里的promise要么是fulfilled要么是rejected。
  3. 遍历promises数组,调用[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数组同值得数组。
  1. + (FBLPromise<NSArray *> *)onQueue:(dispatch_queue_t)queue all:(NSArray *)allPromises {
  2. NSParameterAssert(queue);
  3. NSParameterAssert(allPromises);
  4. if (allPromises.count == 0) {
  5. return [[FBLPromise alloc] initWithResolution:@[]];
  6. }
  7. NSMutableArray *promises = [allPromises mutableCopy];
  8. return [FBLPromise
  9. onQueue:queue
  10. async:^(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock reject) {
  11. for (NSUInteger i = 0; i < promises.count; ++i) {
  12. id promise = promises[i];
  13. if ([promise isKindOfClass:self]) {
  14. continue;
  15. } else if ([promise isKindOfClass:[NSError class]]) {
  16. reject(promise);
  17. return;
  18. } else {
  19. [promises replaceObjectAtIndex:i
  20. withObject:[[FBLPromise alloc] initWithResolution:promise]];
  21. }
  22. }
  23. for (FBLPromise *promise in promises) {
  24. // fulfill()和reject()会被调用多次,但只有第一次有效(被then或者catch捕获)
  25. [promise observeOnQueue:queue
  26. fulfill:^(id __unused _) {
  27. // Wait until all are fulfilled. 同时也是在这个地方筛选rejected promise
  28. for (FBLPromise *promise in promises) {
  29. if (!promise.isFulfilled) {
  30. return;
  31. }
  32. }
  33. // If called multiple times, only the first one affects the result.
  34. fulfill([promises valueForKey:NSStringFromSelector(@selector(value))]);
  35. }
  36. reject:^(NSError *error) {
  37. reject(error);
  38. }];
  39. }
  40. }];
  41. }

all:使用

promises的宗旨之一就是为了避免多层嵌套的promise(nesting promises 或者 callback hell回调地狱),比如下面的:

  1. // 1.nesting promises
  2. [[self loadSomething] then:^id _Nullable(NSString * _Nullable somthing) {
  3. return [[self loadAnother] then:^id _Nullable(NSString * _Nullable another) {
  4. return [self doSomethingWith:somthing andAnother:another];
  5. }];
  6. }];

Promise提供了all来规避avoid nesting promises,同时又可以在多线程异步执行多个任务

  1. [[FBLPromise all:@[[self loadSomething],[self loadAnother]]] then:^id _Nullable(NSArray * _Nullable value) {
  2. return [self doSomethingWith:value.firstObject andAnother:value.lastObject];
  3. }];

如果觉得all的可读性不强,换成方法嵌套,一层一层拆成独立的方法,合理的做法是一个方法包含一个then。但是这种做法还是会有多层嵌套,建议试着去简化all

  1. [[self loadSomething] then:^id _Nullable(NSString * _Nullable value) {
  2. return [self loadAnotherWithSomething:value];
  3. }];
  1. - (FBLPromise <NSString *>*)loadSomething {
  2. return FBLPromise.resolved(@"loadSomething");
  3. }
  4. - (FBLPromise <NSString *>*)loadAnother {
  5. return FBLPromise.resolved(@"loadAnother");
  6. }
  7. - (FBLPromise<NSString *> *)loadAnotherWithSomething:(NSString *)something {
  8. return [[self loadAnother] then:^id(NSString *another) {
  9. return [self doSomethingWith:something andAnother:another];
  10. }];
  11. }
  12. - (id)doSomethingWith:(NSString *)sth andAnother:(NSString *)ant {
  13. return [sth stringByAppendingString:ant];
  14. }

always:

always:源码

无论promise是fulfilled还是rejected,always都会被调用,只是不会带有promise绑定的数据,是个无参block

  1. - (FBLPromise *)onQueue:(dispatch_queue_t)queue always:(FBLPromiseAlwaysWorkBlock)work {
  2. return [self chainOnQueue:queue
  3. chainedFulfill:^id(id value) {
  4. work();
  5. return value;
  6. }
  7. chainedReject:^id(NSError *error) {
  8. work();
  9. return error;
  10. }];
  11. }

always:使用

regardless of fulfilled or rejected, always invoke

  1. __unused FBLPromise * alwaysPromise = [[[[FBLPromise resolvedWith:@"always"] then:^id _Nullable(NSArray * _Nullable value) {
  2. NSLog(@"then : %@",value);
  3. return nil;
  4. }] catch:^(NSError * _Nonnull error) {
  5. NSLog(@"catch : %@",error);
  6. }] always:^{
  7. NSLog(@"alway invoke !");
  8. }];

any:

any:源码

  1. 外部传入promise数组或者说future/solution数组
  2. 通过async实例一个promise返回给外部,FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock reject则在方法内部调用更改状态。
  3. 通过[promises replaceObjectAtIndex: withObject:[[FBLPromise alloc] initWithResolution:promise]];将future/solution数组转成真正的promise数组。
  4. 给转换后的promise数组的每个promise添加监听,[promise observeOnQueue: fulfill: reject:]
  5. 在observer的fulfill和reject block中,都有for循环,首先:所有promises都是是resolved才可以继续执行下一步,所有promises都是rejected,都调用reject(error)用第一个error更改[步骤2]传给外部的promise的状态(pending -> rejected),可以被catch到,否则(至少有一个fulfilled)就会调用fulfill(FBLPromiseCombineValuesAndErrors(promises));更改[步骤2]实例的promise的状态(pending -> fulfilled),可以在外部调用then。
  1. // 提取NSArray<FBLPromise *> *promises数组中的promise.value或者promise.error,重新包装成数组返回
  2. static NSArray *FBLPromiseCombineValuesAndErrors(NSArray<FBLPromise *> *promises) {
  3. NSMutableArray *combinedValuesAndErrors = [[NSMutableArray alloc] init];
  4. for (FBLPromise *promise in promises) {
  5. if (promise.isFulfilled) {
  6. [combinedValuesAndErrors addObject:promise.value ?: [NSNull null]];
  7. continue;
  8. }
  9. if (promise.isRejected) {
  10. [combinedValuesAndErrors addObject:promise.error];
  11. continue;
  12. }
  13. assert(!promise.isPending);
  14. };
  15. return combinedValuesAndErrors;
  16. }
  17. + (FBLPromise<NSArray *> *)onQueue:(dispatch_queue_t)queue any:(NSArray *)anyPromises {
  18. NSMutableArray *promises = [anyPromises mutableCopy];
  19. return [FBLPromise
  20. onQueue:queue
  21. async:^(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock reject) {
  22. for (NSUInteger i = 0; i < promises.count; ++i) {
  23. id promise = promises[i];
  24. if ([promise isKindOfClass:self]) {
  25. continue;
  26. } else {
  27. [promises replaceObjectAtIndex:i
  28. withObject:[[FBLPromise alloc] initWithResolution:promise]];
  29. }
  30. }
  31. for (FBLPromise *promise in promises) {
  32. [promise observeOnQueue:queue
  33. fulfill:^(id __unused _) {
  34. // Wait until all are resolved.
  35. for (FBLPromise *promise in promises) {
  36. if (promise.isPending) {
  37. return;
  38. }
  39. }
  40. // If called multiple times, only the first one affects the result.
  41. fulfill(FBLPromiseCombineValuesAndErrors(promises));
  42. }
  43. reject:^(NSError *error) {
  44. BOOL atLeastOneIsFulfilled = NO;
  45. for (FBLPromise *promise in promises) {
  46. if (promise.isPending) {
  47. return;
  48. }
  49. if (promise.isFulfilled) {
  50. atLeastOneIsFulfilled = YES;
  51. }
  52. }
  53. if (atLeastOneIsFulfilled) {
  54. fulfill(FBLPromiseCombineValuesAndErrors(promises));
  55. } else {
  56. reject(error);
  57. }
  58. }];
  59. }
  60. }];
  61. }

any:使用

上面提到了,any数组中的future/resolution要么全部非法rejected,能被catch;其它情况都可以调用then获取数组中的future,只不过需要区分error和其它值。any的应用场景非常少

  1. allArr = @[errorResolution];
  2. [[[FBLPromise any:allArr] then:^id _Nullable(NSArray * _Nullable value) {
  3. NSLog(@"any : %@",value);
  4. // 需要解析数组里的数据,区分正常数据和error
  5. if ([value.firstObject isKindOfClass:NSError.class]) {
  6. NSLog(@"dosomthing");
  7. } else if ([value.lastObject isKindOfClass:NSError.class]) {
  8. NSLog(@"dosomthing");
  9. }
  10. return nil;
  11. }] catch:^(NSError * _Nonnull error) {
  12. NSLog(@"any will never be catched : %@",error);
  13. }];

FBLPromiseAwait

FBLPromiseAwait源码

使用信号量机制dispatch_semaphore_t实现await,利用锁实现等待上一个promise被resolve。

  1. id __nullable FBLPromiseAwait(FBLPromise *promise, NSError **outError) {
  2. assert(promise);
  3. static dispatch_once_t onceToken;
  4. static dispatch_queue_t queue;
  5. dispatch_once(&onceToken, ^{
  6. queue = dispatch_queue_create("com.google.FBLPromises.Await", DISPATCH_QUEUE_CONCURRENT);
  7. });
  8. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  9. id __block resolution;
  10. NSError __block *blockError;
  11. [promise chainOnQueue:queue
  12. chainedFulfill:^id(id value) {
  13. resolution = value;
  14. dispatch_semaphore_signal(semaphore);
  15. return value;
  16. }
  17. chainedReject:^id(NSError *error) {
  18. blockError = error;
  19. dispatch_semaphore_signal(semaphore);
  20. return error;
  21. }];
  22. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  23. if (outError) {
  24. *outError = blockError;
  25. }
  26. return resolution;
  27. }

FBLPromiseAwait使用

FBLPromiseAwait内部是信号量机制,会锁当前的线程,所以FBLPromiseAwait()所在的线程和等待resolve的promise的执行线程尽量不要是同一线程,不然可能造成死锁。
如果FBLPromiseAwait在线程A上等待,FBLPromiseAwait等待的promise刚好在线程A上等待fulfill或者reject,就会导致死锁。promise的默认线程是主线程,所以要特别注意,比如下面的代码就会造成死锁,而且锁主线程。

  1. [FBLPromise onQueue:FBLPromise.defaultDispatchQueue do:^id _Nullable{
  2. NSError * error;
  3. id result = FBLPromiseAwait([self someAsyncRoutine], &error);
  4. return error ?: result;
  5. }];
  6. // ====== Await 仿耗时任务
  7. - (FBLPromise *)someAsyncRoutine {
  8. dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
  9. FBLPromise * promise = [FBLPromise pendingPromise];
  10. int64_t const timeToWait = (int64_t)(4 * NSEC_PER_SEC);
  11. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeToWait),
  12. queue, ^{
  13. dispatch_async(FBLPromise.defaultDispatchQueue, ^{
  14. [promise fulfill:@"await"];
  15. });
  16. });
  17. return promise;
  18. }

所以尽量确保FBLPromiseAwait等待的所在线程和promise执行resolve的线程不是同一条,比如下面的用法。FBLPromiseAwait尽量不要在主线程执行,防止锁主线程

  1. dispatch_queue_t asyncQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
  2. [FBLPromise onQueue:asyncQueue do:^id _Nullable{
  3. NSError * error;
  4. id result = FBLPromiseAwait([self someAsyncRoutine], &error);
  5. return error ?: result;
  6. }];

delay:

delay:源码

其核心代码就是下面的dispatch_after,延迟调用[promise fulfill:value],所以外部的then会延迟响应,但是[promise reject:error];不会延迟调用,所以外部的catch会立刻调用。

  1. - (FBLPromise *)onQueue:(dispatch_queue_t)queue delay:(NSTimeInterval)interval {
  2. FBLPromise *promise = [[FBLPromise alloc] initPending];
  3. [self observeOnQueue:queue
  4. fulfill:^(id __nullable value) {
  5. dispatch_after(dispatch_time(0, (int64_t)(interval * NSEC_PER_SEC)), queue, ^{
  6. [promise fulfill:value];
  7. });
  8. }
  9. reject:^(NSError *error) {
  10. [promise reject:error];
  11. }];
  12. return promise;
  13. }

delay:使用

可以实现一些延迟操作

  1. FBLPromise * delayPromise = [[FBLPromise do:^id _Nullable{
  2. NSLog(@"开始调用%@",NSDate.date);
  3. return @"delay";
  4. }] delay:2.0];
  5. [delayPromise then:^id _Nullable(id _Nullable value) {
  6. NSLog(@"延迟2秒后调用%@",NSDate.date);
  7. return value;
  8. }];

race:

race:延迟

async:里面虽然有2个for循环,功能是相同的,但由于promise是一次性的,所以race:中return的promise是会由数组中最先resolve的promise决定状态。注意:promise可能在多线程实现,最先resolve的不一定是数组第一个。

  1. + (instancetype)onQueue:(dispatch_queue_t)queue race:(NSArray *)racePromises {
  2. NSArray *promises = [racePromises copy];
  3. return [FBLPromise onQueue:queue
  4. async:^(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock reject) {
  5. for (id promise in promises) {
  6. if (![promise isKindOfClass:self]) {
  7. fulfill(promise);
  8. return;
  9. }
  10. }
  11. // Subscribe all, but only the first one to resolve will change
  12. // the resulting promise's state.
  13. for (FBLPromise *promise in promises) {
  14. [promise observeOnQueue:queue fulfill:fulfill reject:reject];
  15. }
  16. }];
  17. }

race:使用

有一种说法:多个异步任务是为了容错,比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。

  1. [[FBLPromise race:@[[FBLPromise resolvedWith:@"race1"],[FBLPromise resolvedWith:@"race2"]]] then:^id _Nullable(id _Nullable value) {
  2. NSLog(@"race: %@",value); // race: race1
  3. return value;
  4. }];

recover:纠错恢复

recover:源码

增加一个监听当前promise将要被reject的promise,即当前的promise将要被reject否决,会触发return recovery(error);,将error传给外部,并将recovery返回值继续返回给当前的promise作为future,以此实现纠错。

  1. - (FBLPromise *)onQueue:(dispatch_queue_t)queue recover:(FBLPromiseRecoverWorkBlock)recovery {
  2. return [self chainOnQueue:queue
  3. chainedFulfill:nil
  4. chainedReject:^id(NSError *error) {
  5. return recovery(error);
  6. }];
  7. }

recover:使用

  1. [[[self someErrorPromise] recover:^id _Nullable(NSError * _Nonnull error) {
  2. NSLog(@"handle some error :%@ ",error);
  3. return @"a fulfilled resolution";
  4. }] then:^id _Nullable(id _Nullable value) {
  5. NSLog(@"got a value %@",value);
  6. return value;
  7. }];

reduce:combine:

reduce:源码

基于当前的promise和items数据源,创建一个扁平的promise链,promise <- promiseA <- promiseB <- promiseC ~。在for循环中将上一个promise的solution和当前item传入reducer(value, item),外部按照一定的规则生成新的value传给当前的promise。最终返回的promise是数组最后一个Item转换的promise。

  1. - (FBLPromise *)onQueue:(dispatch_queue_t)queue
  2. reduce:(NSArray *)items
  3. combine:(FBLPromiseReducerBlock)reducer {
  4. FBLPromise *promise = self;
  5. for (id item in items) {
  6. promise = [promise chainOnQueue:queue
  7. chainedFulfill:^id(id value) {
  8. return reducer(value, item);
  9. }
  10. chainedReject:nil];
  11. }
  12. return promise;
  13. }

reduce:使用

比如将数组拼接成一个扁平的字符串 @"0" + @[@1, @2, @3] -> @"0, 1, 2, 3"

  1. NSArray<NSNumber *> *numbers = @[ @1, @2, @3 ];
  2. FBLPromise * aPromise = [FBLPromise resolvedWith:@"0"];
  3. [[aPromise reduce:numbers combine:^id(NSString *partialString, NSNumber *nextNumber) {
  4. // partialString == 0, return 0,1
  5. // partialString == 0,1, return 0,1,2
  6. // partialString == 0,1,2, return 0,1,2,3
  7. // 相当于转换成4个链接的promise,@0也是第一个
  8. // 0: [FBLPromise resolvedWith:@"0"] value: 0
  9. // 1-3 即数组转成成的promise链,fulfill(1 or 2 or 3)
  10. return [NSString stringWithFormat:@"%@, %@", partialString, nextNumber.stringValue];
  11. }] then:^id(NSString *string) {
  12. // then捕获的就是promises链的最后一个:@3
  13. // Final result = 0, 1, 2, 3
  14. NSLog(@"Final result = %@", string);
  15. return nil;
  16. }];

retry:

retry: 源码

正常执行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)

  1. NSInteger const FBLPromiseRetryDefaultAttemptsCount = 1;
  2. NSTimeInterval const FBLPromiseRetryDefaultDelayInterval = 1.0;
  3. static void FBLPromiseRetryAttempt(FBLPromise *promise, dispatch_queue_t queue, NSInteger count,
  4. NSTimeInterval interval, FBLPromiseRetryPredicateBlock predicate,
  5. FBLPromiseRetryWorkBlock work) {
  6. __auto_type retrier = ^(id __nullable value) {
  7. if ([value isKindOfClass:[NSError class]]) {
  8. if (count <= 0 || (predicate && !predicate(count, value))) {
  9. [promise reject:value];
  10. } else {
  11. dispatch_after(dispatch_time(0, (int64_t)(interval * NSEC_PER_SEC)), queue, ^{
  12. FBLPromiseRetryAttempt(promise, queue, count - 1, interval, predicate, work);
  13. });
  14. }
  15. } else {
  16. [promise fulfill:value];
  17. }
  18. };
  19. id value = work();
  20. if ([value isKindOfClass:[FBLPromise class]]) {
  21. [(FBLPromise *)value observeOnQueue:queue fulfill:retrier reject:retrier];
  22. } else {
  23. retrier(value);
  24. }
  25. }
  26. + (FBLPromise *)onQueue:(dispatch_queue_t)queue
  27. attempts:(NSInteger)count
  28. delay:(NSTimeInterval)interval
  29. condition:(nullable FBLPromiseRetryPredicateBlock)predicate
  30. retry:(FBLPromiseRetryWorkBlock)work {
  31. FBLPromise *promise = [[FBLPromise alloc] initPending];
  32. FBLPromiseRetryAttempt(promise, queue, count, interval, predicate, work);
  33. return promise;
  34. }

retry 使用

如果第一次捕获到了error,默认重复1次,且延迟1秒后执行重复任务

  1. NSURL *url = [NSURL URLWithString:@"https://www.json.org"];
  2. [[[FBLPromise retry:^id _Nullable{
  3. return [self fetchWithURL:url];
  4. }] then:^id _Nullable(id _Nullable value) {
  5. NSLog(@"retry then %@",value);
  6. return nil;
  7. }] catch:^(NSError * _Nonnull error) {
  8. NSLog(@"catched some error: %@",error);
  9. }];
  10. // 自定义retry的队列、次数、延迟时间、条件
  11. [[[FBLPromise onQueue:gQueue attempts:1 delay:1 condition:^BOOL(NSInteger count, NSError * _Nonnull value) {
  12. // condition 返回false直接走reject,被catch捕获,返回true,延迟后执行重复retry
  13. return true;
  14. } retry:^id _Nullable{
  15. return [self fetchWithURL:url];
  16. }] then:^id _Nullable(id _Nullable value) {
  17. NSLog(@"retry then %@",value);
  18. return nil;
  19. }] catch:^(NSError * _Nonnull error) {
  20. NSLog(@"catched some error: %@",error);
  21. }];;
  22. - (FBLPromise <NSArray *> *)fetchWithURL:(NSURL *)url {
  23. return [FBLPromise wrap2ObjectsOrErrorCompletion:^(FBLPromise2ObjectsOrErrorCompletion _Nonnull handler) {
  24. // [[NSURLSession.sharedSession dataTaskWithURL:url completionHandler:handler] resume];
  25. handler(nil, nil, [NSError errorWithDomain:NSCocoaErrorDomain code:100 userInfo:@{NSURLErrorFailingURLErrorKey:@"fetchWithURL Error"}]);
  26. }];
  27. }

timeout:

timeout:源码

可见在等待promise被resolve时启用了一个延迟任务,因为promise是一次性的,所以promise超时就会先触发dispatch_after,并返回一个自定义的error(FBLPromiseErrorDomain and FBLPromiseErrorCodeTimedOut)。

  1. typedef NS_ENUM(NSInteger, FBLPromiseErrorCode) {
  2. /** Promise failed to resolve in time. */
  3. FBLPromiseErrorCodeTimedOut = 1,
  4. /** Validation predicate returned false. */
  5. FBLPromiseErrorCodeValidationFailure = 2,
  6. }
  7. - (FBLPromise *)onQueue:(dispatch_queue_t)queue timeout:(NSTimeInterval)interval {
  8. NSParameterAssert(queue);
  9. FBLPromise *promise = [[FBLPromise alloc] initPending];
  10. [self observeOnQueue:queue
  11. fulfill:^(id __nullable value) {
  12. [promise fulfill:value];
  13. }
  14. reject:^(NSError *error) {
  15. [promise reject:error];
  16. }];
  17. typeof(self) __weak weakPromise = promise;
  18. dispatch_after(dispatch_time(0, (int64_t)(interval * NSEC_PER_SEC)), queue, ^{
  19. NSError *timedOutError = [[NSError alloc] initWithDomain:FBLPromiseErrorDomain
  20. code:FBLPromiseErrorCodeTimedOut
  21. userInfo:nil];
  22. [weakPromise reject:timedOutError];
  23. });
  24. return promise;
  25. }

timeout:使用

注意点:需要判断error的code,[error.domain isEqual:FBLPromiseErrorDomain],以区分正常的reject error。

  1. // 仿耗时任务
  2. FBLPromise * promise = [[FBLPromise wrap2ObjectsOrErrorCompletion:^(FBLPromise2ObjectsOrErrorCompletion _Nonnull handler) {
  3. dispatch_after(dispatch_time(0, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  4. handler(nil, nil, [NSError errorWithDomain:NSCocoaErrorDomain code:10010 userInfo:@{NSURLErrorFailingURLErrorKey:@"timeout"}]);
  5. });
  6. }] timeout:5];
  7. [[promise then:^id _Nullable(id _Nullable value) {
  8. NSLog(@"timeout超时%@",value);
  9. return value;
  10. }] catch:^(NSError * _Nonnull error) {
  11. if ([error.domain isEqual:FBLPromiseErrorDomain] || error.code == FBLPromiseErrorCodeTimedOut) {
  12. NSLog(@"timeout超时:%@",error);
  13. } else {
  14. NSLog(@"timeout出错:%@",error);
  15. }
  16. }];

validate:

validate:源码

如果promise将被fulfill,会触发FBLPromiseValidateWorkBlock校验,如果predicate校验通过,则继续执行fulfill(value),校验不通过则fulfill(error),这个error是自定义的(FBLPromiseErrorDomain and FBLPromiseErrorCodeValidationFailure)。

  1. typedef NS_ENUM(NSInteger, FBLPromiseErrorCode) {
  2. /** Promise failed to resolve in time. */
  3. FBLPromiseErrorCodeTimedOut = 1,
  4. /** Validation predicate returned false. */
  5. FBLPromiseErrorCodeValidationFailure = 2,
  6. }
  7. - (FBLPromise*)onQueue:(dispatch_queue_t)queue validate:(FBLPromiseValidateWorkBlock)predicate {
  8. NSParameterAssert(queue);
  9. NSParameterAssert(predicate);
  10. FBLPromiseChainedFulfillBlock chainedFulfill = ^id(id value) {
  11. return predicate(value) ? value :
  12. [[NSError alloc] initWithDomain:FBLPromiseErrorDomain
  13. code:FBLPromiseErrorCodeValidationFailure
  14. userInfo:nil];
  15. };
  16. return [self chainOnQueue:queue chainedFulfill:chainedFulfill chainedReject:nil];
  17. }

validate:使用

Validate可以理解校验是否跟预期的value一致

  1. [[[[self getStringAtURL:url] validate:^BOOL(NSString * _Nullable value) {
  2. return [value isEqualToString:@"some str"];
  3. }] then:^id _Nullable(NSString * _Nullable value) {
  4. return [self doSomethingWith:value andAnother:value];
  5. }] catch:^(NSError * _Nonnull error) {
  6. if ([error.domain isEqual:FBLPromiseErrorDomain] || error.code == FBLPromiseErrorCodeValidationFailure) {
  7. NSLog(@"validate 出错1 :%@",error);
  8. } else {
  9. NSLog(@"validate 出错2 :%@",error);
  10. }
  11. }];

wrap

wrap源码之一

 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做拓展。

  1. + (instancetype)onQueue:(dispatch_queue_t)queue
  2. wrapCompletion:(void (^)(FBLPromiseCompletion handler))work {
  3. NSParameterAssert(queue);
  4. NSParameterAssert(work);
  5. return [self onQueue:queue
  6. async:^(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock __unused _) {
  7. work(^{
  8. fulfill(nil);
  9. });
  10. }];
  11. }

wrap使用

wrap方便自定义耗时任务的promise,如果网络请求,promise提供了较大的拓展空间

  1. // 耗时任务
  2. [[[FBLPromise wrap2ObjectsOrErrorCompletion:^(FBLPromise2ObjectsOrErrorCompletion _Nonnull handler) {
  3. dispatch_after(dispatch_time(0, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  4. handler(nil, nil, [NSError errorWithDomain:NSCocoaErrorDomain code:10010 userInfo:@{NSURLErrorFailingURLErrorKey:@"timeout"}]);
  5. });
  6. }]
  7. [[[FBLPromise wrapErrorCompletion:^(FBLPromiseErrorCompletion _Nonnull handler) {
  8. // handler([NSError errorWithDomain:NSCocoaErrorDomain code:101010 userInfo:nil]);
  9. handler(nil);
  10. }] recover:^id _Nullable(NSError * _Nonnull error) {
  11. return @"solution";
  12. }] then:^id _Nullable(id _Nullable value) {
  13. NSLog(@"wrapErrorCompletion value is nil if not recover. %@",value);
  14. return value;
  15. }];
  16. [[[FBLPromise wrapErrorOrObjectCompletion:^(FBLPromiseErrorOrObjectCompletion _Nonnull handler) {
  17. handler([NSError errorWithDomain:NSCocoaErrorDomain code:101010 userInfo:nil], @"dwadwa");
  18. }] recover:^id _Nullable(NSError * _Nonnull error) {
  19. return @"solution";
  20. }] then:^id _Nullable(id _Nullable value) {
  21. NSLog(@"wrapErrorOrObjectCompletion value %@",value);
  22. return value;
  23. }];
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注