废话不多说,本博就开工。
源码地址
本博客没有关注swift ,因此还是采用oc方式分析源码,源码来自pod 更新
pod 'PromiseKit', '~> 1.0'
概述
promise 是用来处理回调地狱的,借鉴图
image
基本使用
本文看alert 的promise
UIAlertView * alertview=[[UIAlertView alloc]initWithTitle:@"alert" message:@"promiseTest" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
[alertview promise].then(^{
NSLog(@"dddd");
});
结果呢
image.png
这是怎么实现的呢?进入正题。
promise 源码实现
引出核心代码
@interface PMKAlertViewDelegater : NSObject <UIAlertViewDelegate> {
@public
void (^fulfiller)(id);
}
@end
@implementation PMKAlertViewDelegater
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
fulfiller(PMKManifold(@(buttonIndex), alertView));
PMKRelease(self);
}
@end
@implementation UIAlertView (PromiseKit)
- (PMKPromise *)promise {
PMKAlertViewDelegater *d = [PMKAlertViewDelegater new];
PMKRetain(d);
self.delegate = d;
[self show];
return [PMKPromise new:^(id fulfiller, id rejecter){
d->fulfiller = fulfiller;
}];
}
@end
从上面我们UIAlertView Category 中我们能看出来,这里调用- (PMKPromise *)promise
1.我们会创建UIAlertView的一个代理并且Retain
- 将对象设置为UIAlertView 的代理
3.展示Alert
4.生成一个promise
这里重点是第四步生成promise。
这里我们可以看看其他零散的小知识点宏定义
extern void *PMKManualReferenceAssociatedObject;
#define PMKRetain(obj) objc_setAssociatedObject(obj, PMKManualReferenceAssociatedObject, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
#define PMKRelease(obj) objc_setAssociatedObject(obj, PMKManualReferenceAssociatedObject, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
这里PMKRetain 都是采用关联引用进行保存对象
PMKRelease 采用关联引用对对象近视释放 ,成对出现的。
这里还有个限制参数的宏定义,编译阶段就知道了参数的个数
#define PMKManifold(...) __PMKManifold(__VA_ARGS__, 3, 2, 1)
#define __PMKManifold(_1, _2, _3, N, ...) __PMKArrayWithCount(N, _1, _2, _3)
extern id __PMKArrayWithCount(NSUInteger, ...);
这里 ... 就是用VA_ARGS 代表的,代表无限参数
怎么看这个宏定义呢?我们就是把参数带人一步步看就知道了
假设我们的参数是@1 ,@2,@3 这个宏定义运行的结果是 __PMKArrayWithCount(3, @1, @2, @3);可以帮助我们自动获取参数的个数,不过这里最多只支持三个
1 PMKManifold(@1 ,@2,@3) 转换成__PMKManifold(@1 ,@2,@3,3,2,1) 把 VA_ARGS 换成 @1 ,@2,@3*
__PMKManifold(@1 ,@2,@3,3,2,1) 转换成 __PMKArrayWithCount(3, @1 ,@2,@3) 这里N 是3
这是宏定义的二次处理。编译阶段我们就知道参数的个数了。性能能改善一点是一点吧。
核心源码类PMKPromise
PMKPromise 类结构
这个类结构比较简单
PMKPromise.png
PMKPromise 类初始化
看了API 作者没有标记那个是真正的初始化API
真正生成对象的API是
+ (instancetype)promiseWithResolver:(void (^)(PMKResolver))block
默认的+init 方法给重写设置无效
typedef void (^PMKResolver)(id);
+ (instancetype)promiseWithResolver:(void (^)(PMKResolver))block {
PMKPromise *this = [self alloc];
this->_promiseQueue = PMKCreatePromiseQueue();
this->_handlers = [NSMutableArray new];
@try {
block(^(id result){
if (PMKGetResult(this))
return PMKLog(@"PromiseKit: Warning: Promise already resolved");
PMKResolve(this, result);
});
} @catch (id e) {
// at this point, no pointer to the Promise has been provided
// to the user, so we can’t have any handlers, so all we need
// to do is set _result. Technically using PMKSetResult is
// not needed either, but this seems better safe than sorry.
PMKSetResult(this, NSErrorFromException(e));
}
return this;
}
这里我们看传入的参数只有一个PMKResolver类型的block
1 入参: 传入PMKResolver 类型block
image.png
2 初始化: 创建PMKPromise 对象,分配空间
3 给PMKPromise 对象 的成员变量_promiseQueue设置值。queue 是一个DISPATCH_QUEUE_CONCURRENT 类型的queue 。(Concurrent Dispatch Queue不过创建多少都没有问题,因为Concurrent Dispatch Queue所使用的线程由系统的XNU内核高效管理,不会影响系统性能。)
这里上个图
4创建 数组给 _handlers 赋值
5 try catch 调用 PMKResolver 类型的block 。传入的参数是block ,类型是^(id result){}。 调用block 失败了,调用**static NSArray PMKSetResult(PMKPromise this, id result)。
6 返回this
这里看完好多人肯定一头雾水,这是在干么。其实把,这里是我们只是创建了个PMKPromise ,我们采用了block的保存语句块的功能,将对这个PMKPromise 对象的处理放在了外面。如果外界需要对我处理,那么调用我给你外界的句柄 block。(这里要是传入的参数不对,那么我就直接对这个PMKPromise 做错误处理)
结构如下
image.png
我们先看看要是外界传入的参数block 不合法,我们调用的static NSArray *PMKSetResult(PMKPromise *this, id result)
static NSArray *PMKSetResult(PMKPromise *this, id result) {
__block NSArray *handlers;
result = PMKSanitizeResult(result);
dispatch_barrier_sync(this->_promiseQueue, ^{
handlers = this->_handlers;
this->_result = result;
this->_handlers = nil;
});
return handlers;
}
1 调用PMKSanitizeResult对结果进行处理下,保证结果不是nil。
2 在promise queue中获取下handle ,将handle处理为nil ,将成员变量_result 赋值(一定不是nil,nil代表这个promise还没有被处理,是promise的一种状态)
3 返回handles
这里没有对返回的handle 做处理
我们看看如何处理PMKPromise,外界调用这个block发生了什么事情
^(id result){
if (PMKGetResult(this))
return PMKLog(@"PromiseKit: Warning: Promise already resolved");
PMKResolve(this, result);
}
1.这里很简单, 调用static id PMKGetResult(PMKPromise this) 判断 PMKPromise 是否有结果,有结果就返回。(具体怎么返回看下面)
2 如果* PMKPromise** 没有设置结果就调用*static void PMKResolve(PMKPromise this, id result) 将结果设置到PMKPromise 的变量_result 中(具体函数分析往下面看)
先看*static id PMKGetResult(PMKPromise this)
static id PMKGetResult(PMKPromise *this) {
__block id result;
dispatch_sync(this->_promiseQueue, ^{
result = this->_result;
});
return result;
}
这个很简单,就是获取下PMKPromise 的成员变量 _result的值.不过是同步获取,意思是当前线程中获取下 结果返回。
再看下*static void PMKResolve(PMKPromise this, id result)
static void PMKResolve(PMKPromise *this, id result) {
void (^set)(id) = ^(id r){
NSArray *handlers = PMKSetResult(this, r);
for (void (^handler)(id) in handlers)
handler(r);
};
if (IsPromise(result)) {
PMKPromise *next = result;
dispatch_barrier_sync(next->_promiseQueue, ^{
id nextResult = next->_result;
if (nextResult == nil) { // ie. pending
[next->_handlers addObject:^(id o){
PMKResolve(this, o);
}];
} else
set(nextResult);
});
} else
set(result);
}
这算是对PMKPromise 处理的核心函数了
1 声明一个 block 命名set 。调用这个set block 。这个block中会先调用PMKSetResult,对这个promise 进行赋值操作,然后在处理所有的handle。(这里根据handle的调用和下文知道也是一个block)
2 这里是个宏定义判断result 是不是promise
3 result 不是 promise 那么就调用set block
4 result 是promise,命名为next ,那么获取next这个promise 的 成员变量_result。要是这个promise 还没有设置结果,那么我们next的_handle 成员变量中要保存一个block,这个block是处理this promise的句柄(意思是next 调用set的时候,需要将result 告诉下this)。要是promise 的设置了结果,那么就调用set block
看到这里我估计好多人只是看懂了函数本身,但是整体没有看懂。因此在做个整体图明晰下
image.png
这里举例说明
类的成员变量
{
void (^fulfiller)(id);
PMKPromise * promiseNext;
PMKPromise * promiseThis;
}
promiseThis = [PMKPromise new:^(PMKFulfiller fulfill, PMKRejecter reject) {
fulfill(promiseNext);
}];
UIAlertView * alertview=[[UIAlertView alloc]initWithTitle:@"alert" message:@"promiseTest" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
[alertview promise].then(^{
fulfiller(@"nextPromise");
NSLog(@"%@",promiseThis.value);
});
这里就知道了。handle 中保存的是所有的其他的依赖结果的promise。不单单是一个。可以是多个。
这里我们再看看UIAlertView 调用的这个初始化方法
+ (instancetype)new:(void(^)(PMKFulfiller, PMKRejecter))block {
return [self promiseWithResolver:^(PMKResolver resolve) {
id rejecter = ^(id error){
if (error == nil) {
error = NSErrorFromNil();
} else if (IsPromise(error) && [error rejected]) {
// this is safe, acceptable and (basically) valid
} else if (!IsError(error)) {
id userInfo = @{NSLocalizedDescriptionKey: [error description], PMKUnderlyingExceptionKey: error};
error = [NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:userInfo];
}
resolve(error);
};
id fulfiller = ^(id result){
if (IsError(result))
PMKLog(@"PromiseKit: Warning: PMKFulfiller called with NSError.");
resolve(result);
};
block(fulfiller, rejecter);
}];
}
.这个函数传入带有两个参数的block 这两个参数也同样是个block 。类型分别吃PMKFulfiller 和 PMKRejecter
2 这里构建了两个block 。再将两个block(rejecter 和 fulfiller) 传给外界了。
3 . 要是外界调用 fulfiller block ,调用 PMKResolver 类型block 。从上面的分析我们知道,这个就是promise的正常处理。
4 .要是外界调用rejecter block,这里检查传入的error 要是nil 就生成一个NSError, 要是传入的error 是promise 并且结果是error ,那么认为合法,要是传入的数据不是error,误入调用rejecter,我们我们将这个error 重新包装成一个NSError 类型的error 调用 PMKResolver 类型的resolve 对error进行处理。
这里我们看出来,这里对promise的结果又做了区分,rejecter 回调的结果一定是NSError 或者结果是NSError的promise ,而fulfiller 就是正常的结果。
看到这里我们可以对promise的成员变量做个总结了
1 dispatch_queue_t _promiseQueue; 这个不用过多解释,就是promise的queue
NSMutableArray *_handlers; 这个装有 当前promise要是有结果,需要将这个结果传递给其他的promise。
id _result; 这个结果有几种情况 ,没有值 , NSError,promise和其他id类型对象。(后面还有个PMKError 归类到NSError中)
这里其实就产生了一个链条
image.png
链条的传递必须是链条前端的promise 有结果了,后面的promise才能执行。
promise状态 函数
- (BOOL)pending {
id result = PMKGetResult(self);
if (IsPromise(result)) {
return [result pending];
} else
return result == nil;
}
- (BOOL)resolved {
return PMKGetResult(self) != nil;
}
- (BOOL)fulfilled {
id result = PMKGetResult(self);
return result != nil && !IsError(result);
}
- (BOOL)rejected {
id result = PMKGetResult(self);
return result != nil && IsError(result);
}
- (id)value {
id result = PMKGetResult(self);
if (IsPromise(result))
return [(PMKPromise *)result value];
if ([result isKindOfClass:[PMKArray class]])
return ((PMKArray *)result)[0];
if (result == PMKNull)
return nil;
else
return result;
}
这些函数状态函数都是根据result 来做判断的
1 - (BOOL)pending ,这个状态代表promise有没有被处理过,或者处理后结果还是promise ,但是这个promise 的_result 还是nil。没有被处理过(fulfiller 传入的参数是一个没有被处理过的promise)。
2** (BOOL)resolved** ,这个代表promise是否被解决了。
3- (BOOL)fulfilled, 这个代表这个promise 解决的结果是否是正确的
4 - (BOOL)rejected,这个代表这个promise 解决的结果是不是error
5 - (id)value, 获取promise的值,这里需要注意,要是promose的result 是 PMKPromise ,需要特殊处理,不是直接返回。
promise 链条中的其他函数方法
- (PMKPromise *(^)(id))then;
- (PMKPromise *(^)(id))thenInBackground;
- (PMKPromise *(^)(id))catch;
- (PMKPromise *(^)(void(^)(void)))finally;
- (PMKPromise *(^)(dispatch_queue_t, id))thenOn;
- (PMKPromise *(^)(dispatch_queue_t, id))catchOn;
- (PMKPromise *(^)(dispatch_queue_t, void(^)(void)))finallyOn;
+ (PMKPromise *)promiseWithValue:(id)value;
看上面的函数,要么返回PMKPromise 对象本身,要么返回 返回值是PMKPromise 类型的block。这就是典型的链式调用了。并且我们也能看出来,这些函数类似try catch finally 结构
我们把他们分类看
- (PMKPromise *(^)(id))then ;
typedef PMKPromise *(^PMKResolveOnQueueBlock)(dispatch_queue_t, id block);- (PMKResolveOnQueueBlock)thenOn;
这里 then 函数的返回值是一个block, 传入的参数是 block,返 回值是PMKPromise
而thenOn 返回的返回值也是一个block,不过参数是两个,一个是dispatch_queue_t ,第二个是block
- (PMKPromise *(^)(id))then {
return ^(id block){
return self.thenOn(dispatch_get_main_queue(), block);
};
}
then函数调用thenOn 线程传入的是主线程。
重点看thenOn
- (PMKResolveOnQueueBlock)thenOn {
return [self resolved:^(id result) {
if (IsPromise(result))
return ((PMKPromise *)result).thenOn;
if (IsError(result)) return ^(dispatch_queue_t q, id block) {
return [PMKPromise promiseWithValue:result];
};
return ^(dispatch_queue_t q, id block) {
// HACK we seem to expose some bug in ARC where this block can
// be an NSStackBlock which then gets deallocated by the time
// we get around to using it. So we force it to be malloc'd.
block = [block copy];
return dispatch_promise_on(q, ^{
return pmk_safely_call_block(block, result);
});
};
}
pending:^(id result, PMKPromise *next, dispatch_queue_t q, id block, void (^resolve)(id)) {
if (IsError(result))
PMKResolve(next, result);
else dispatch_async(q, ^{
resolve(pmk_safely_call_block(block, result));
});
}];
}
这里只调用了一个函数
*- (id)resolved:(PMKResolveOnQueueBlock(^)(id result))mkresolvedCallback
pending:(void(^)(id result, PMKPromise next, dispatch_queue_t q, id block, void (^resolver)(id)))mkpendingCallback
传入给这个函数的两个值在介绍上面的这个函数时候介绍
- (id)resolved:(PMKResolveOnQueueBlock(^)(id result))mkresolvedCallback
pending:(void(^)(id result, PMKPromise *next, dispatch_queue_t q, id block, void (^resolver)(id)))mkpendingCallback
{
__block PMKResolveOnQueueBlock callBlock;
__block id result;
dispatch_sync(_promiseQueue, ^{
if ((result = _result))
return;
callBlock = ^(dispatch_queue_t q, id block) {
// HACK we seem to expose some bug in ARC where this block can
// be an NSStackBlock which then gets deallocated by the time
// we get around to using it. So we force it to be malloc'd.
block = [block copy];
__block PMKPromise *next = nil;
dispatch_barrier_sync(_promiseQueue, ^{
if ((result = _result))
return;
__block PMKPromiseFulfiller resolver;
next = [PMKPromise new:^(PMKPromiseFulfiller fulfill, PMKPromiseRejecter reject) {
resolver = ^(id o){
if (IsError(o)) reject(o); else fulfill(o);
};
}];
[_handlers addObject:^(id value){
mkpendingCallback(value, next, q, block, resolver);
}];
});
// next can still be `nil` if the promise was resolved after
// 1) `-thenOn` read it and decided which block to return; and
// 2) the call to the block.
return next ?: mkresolvedCallback(result)(q, block);
};
});
// We could just always return the above block, but then every caller would
// trigger a barrier_sync on the promise queue. Instead, if we know that the
// promise is resolved (since that makes it immutable), we can return a simpler
// block that doesn't use a barrier in those cases.
return callBlock ?: mkresolvedCallback(result);
}
1 这个函数的参数有两个,mkresolvedCallback 和 mkpendingCallback。
2 mkresolvedCallback 参数是个block ,参数是result,返回值是一个PMKResolveOnQueueBlock类型的block,这个类型正好和thenOn的返回值是一个类型。
3 mkpendingCallback 也是一个block,参数有五个,分别是id类型的result ,PMKPromise类型的next,dispatch_queue_t 类型的q,id类型的block和入参是id 返回值是void的block ,命名是resolver。
具体分析下这个函数
1 声明一个变量 callBack 和 result
2 在promise 的_promiseQueue 同步获取下_result ,要是_result 有结果了,那么调用mkresolvedCallback 入参数是result ,那么就会返回thenOn的所需要的返回类型的值。这里我们看看mkresolvedCallback 执行干了什么事情。需要返回thenOn 中看
^(id result) {
if (IsPromise(result))
return ((PMKPromise *)result).thenOn;
if (IsError(result)) return ^(dispatch_queue_t q, id block) {
return [PMKPromise promiseWithValue:result];
};
return ^(dispatch_queue_t q, id block) {
// HACK we seem to expose some bug in ARC where this block can
// be an NSStackBlock which then gets deallocated by the time
// we get around to using it. So we force it to be malloc'd.
block = [block copy];
return dispatch_promise_on(q, ^{
return pmk_safely_call_block(block, result);
});
};
}
这里我们知道传入的result 是当前promise 的_result 值。
判断要是 result 是 promise,那么就接着调用 result的thenOn 。
要是result 是 error,那么我们返回一个PMKResolveOnQueueBlock类型的block,这里需要注意的是执行这个block,直接返回带有result结果的PMKPromise 。(因为是error,block都给抛弃掉了)
正常的result结果,那我们返回一个PMKResolveOnQueueBlock类型的block。这里需要注意,我们要是执行这个block,那么我们首先要对参数block执行下copy(参数block可能在栈上,被释放),调用PMKPromise dispatch_promise_on(dispatch_queue_t queue, id block) 返回一个PMKpromise.
这里还有个函数id pmk_safely_call_block(id frock, id result)*。这个函数就是对block的解析
id pmk_safely_call_block(id frock, id result) {
assert(frock);
if (result == PMKNull)
result = nil;
@try {
NSMethodSignature *sig = NSMethodSignatureForBlock(frock);
const NSUInteger nargs = sig.numberOfArguments;
const char rtype = sig.methodReturnType[0];
#define call_block_with_rtype(type) ({^type{ \
switch (nargs) { \
case 1: \
return ((type(^)(void))frock)(); \
case 2: { \
const id arg = [result class] == [PMKArray class] ? ((PMKArray *)result)[0] : result; \
return ((type(^)(id))frock)(arg); \
} \
case 3: { \
type (^block)(id, id) = frock; \
return [result class] == [PMKArray class] \
? block(((PMKArray *)result)[0], ((PMKArray *)result)[1]) \
: block(result, nil); \
} \
case 4: { \
type (^block)(id, id, id) = frock; \
return [result class] == [PMKArray class] \
? block(((PMKArray *)result)[0], ((PMKArray *)result)[1], ((PMKArray *)result)[2]) \
: block(result, nil, nil); \
} \
default: \
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"PromiseKit: The provided block’s argument count is unsupported." userInfo:nil]; \
}}();})
switch (rtype) {
case 'v':
call_block_with_rtype(void);
return PMKNull;
case '@':
return call_block_with_rtype(id) ?: PMKNull;
case '*': {
char *str = call_block_with_rtype(char *);
return str ? @(str) : PMKNull;
}
case 'c': return @(call_block_with_rtype(char));
case 'i': return @(call_block_with_rtype(int));
case 's': return @(call_block_with_rtype(short));
case 'l': return @(call_block_with_rtype(long));
case 'q': return @(call_block_with_rtype(long long));
case 'C': return @(call_block_with_rtype(unsigned char));
case 'I': return @(call_block_with_rtype(unsigned int));
case 'S': return @(call_block_with_rtype(unsigned short));
case 'L': return @(call_block_with_rtype(unsigned long));
case 'Q': return @(call_block_with_rtype(unsigned long long));
case 'f': return @(call_block_with_rtype(float));
case 'd': return @(call_block_with_rtype(double));
case 'B': return @(call_block_with_rtype(_Bool));
case '^':
if (strcmp(sig.methodReturnType, "^v") == 0) {
call_block_with_rtype(void);
return PMKNull;
}
// else fall through!
default:
@throw PMKE(@"Unsupported method signature… Why not fork and fix?");
}
} @catch (id e) {
#ifdef PMK_RETHROW_LIKE_A_MOFO
if ([e isKindOfClass:[NSException class]] && (
[e name] == NSGenericException ||
[e name] == NSRangeException ||
[e name] == NSInvalidArgumentException ||
[e name] == NSInternalInconsistencyException ||
[e name] == NSObjectInaccessibleException ||
[e name] == NSObjectNotAvailableException ||
[e name] == NSDestinationInvalidException ||
[e name] == NSPortTimeoutException ||
[e name] == NSInvalidSendPortException ||
[e name] == NSInvalidReceivePortException ||
[e name] == NSPortSendException ||
[e name] == NSPortReceiveException))
@throw e;
#endif
return NSErrorFromException(e);
}
}
这个方法传入的参数有两个,第一个参数是block,第二个参数是返回的结果(链条中上一个promise产生的结果)。
这个方法是将block 转换成函数签名,通过函数签名方法获取这个block的参数
static NSMethodSignature *NSMethodSignatureForBlock(id block) 这个方法是对block进行方法解析出签名,将block 转换成函数签名。这样我们就通过这个函数签名知道了这个block的参数以及返回值,知道了参数和返回值,我们就可以自己构造block了。
PMKPromise *dispatch_promise_on(dispatch_queue_t queue, id block) {
return [PMKPromise new:^(void(^fulfiller)(id), void(^rejecter)(id)){
dispatch_async(queue, ^{
id result = pmk_safely_call_block(block, nil);
if (IsError(result))
rejecter(result);
else
fulfiller(result);
});
}];
}
1.首先明确一点,这个函数返回一个PMKPromise
2 . 调用+ (instancetype)new:(void(^)(PMKFulfiller, PMKRejecter))block 函数,这个函数前面分析过会给外界执行本fulfiller 和 rejecter 的指针
3.这里获取到fulfiller 和 rejecter 的指针没有做保留处理,直接执行了。(pmk_safely_call_block 就是对block进行解析执行)
4 ** pmk_safely_call_block** 执行的block 是 在thenOn(行数323行的block),
return pmk_safely_call_block(block, result);
看到这里想明白了就知道了323行的这个block是外界传入的block,result是front 的promise的结果,意思是把front 的promise的结果传递给我们thenOn 后面跟人的block
到这里,我们分析完毕了要是front 的promise 有结果的回调顺序
总结下如图
FlowchartDiagram1.png
要是front promise 没有结果呢。这说明front promise还没有结果传递过来。
返回到- (id)resolved:(PMKResolveOnQueueBlock(^)(id result))mkresolvedCallback
pending:(void(^)(id result, PMKPromise *next, dispatch_queue_t q, id block, void (^resolver)(id)))mkpendingCallback函数
1.生成PMKResolveOnQueueBlock类型的一个block,给callBack赋值。
2.并且把这个新生成的block返回。暂时标记为block2 。这个block2 接受thenOn传入的的dispatch_queue_t q, id block
我们看看 block2 干了啥事情
1 copy 将栈上的block copy到堆上
2 同步在当前队列上看promise的_result,有_result,直接返回,调用mkresolvedCallback(result)(q, block); 结束。(这里看着有点头大,不过我们分开看就好看了多了,mkresolvedCallback(result) 返回的是PMKResolveOnQueueBlock ,PMKResolveOnQueueBlock执行需要两个参数 dispatch_queue_t q, id block ,因此后面又有两个括号跟着两个参数。其实和刚开始没有参数的逻辑是一样的)
3 同步在当前队列上看promise的_result ,没有_result .那么我们就重新生成一个PMKpromise 对象,将这个对象返回。这里需要注意的是
next = [PMKPromise new:^(PMKPromiseFulfiller fulfill, PMKPromiseRejecter reject) {
resolver = ^(id o){
if (IsError(o)) reject(o); else fulfill(o);
};
}];
[_handlers addObject:^(id value){
mkpendingCallback(value, next, q, block, resolver);
}];
因为current的promise 还没有结果,所以,我们新生成的next promise 的 Fulfiller 和Rejecter 都不能执行,需要让current promise保存next promise 的Fulfiller 和Rejecter ,当current promise 有结果了,将结果传递给next promise 的Fulfiller 或者Rejecter。
我们知道,promise链条的传递是通过_handle 来传递的,_handle 中保存的是一个block,类型是^(id value){},参数是current promise的结果,当promise 有结果的时候,会调用_handle中的所有block。因此这里我们需要将next promise 的Fulfiller 和Rejecter 加入到current promise的_handle中去。
这里我们保存next promise 的fulfiller 和Rejecter 是通过一个block 来保存的。这个block 很简单,参数就一个就是结果了,根据结果选择fulfiller 和Rejecter 执行
这里我们看看当_handles 里面的block 执行的时候干了什么事情。当block 执行的时候,我们会调用到mkpendingCallback block 这个block 传入的参数比较多,value就是current promise的result , next 就是next Promise ,q 是next 的dispatch_queue_t,block 是thenOn传入的block,resolver 保存的是next 的fulfiller 和Rejecter。
我们看看mkpendingCallback的实现
^(id result, PMKPromise *next, dispatch_queue_t q, id block, void (^resolve)(id)) {
if (IsError(result))
PMKResolve(next, result);
else dispatch_async(q, ^{
resolve(pmk_safely_call_block(block, result));
});
}
实现很简单了,判断result 是 error ,那么直接更新next promise 的结果,接着传递result 就是了
要是result不是error,是正常返回值,那么我们切换到next 的的queue中,将result 结果传递到thenOn 传入的block 中(pmk_safely_call_block(block, result)),接着调用resolve block,执行next promise 的fulfiller 和Rejecter。调用结束
总结下没有结果的情况
thenOnNotResult.png
到此,thenOn 的完整代码分析完毕。
总结下- (id)resolved:(PMKResolveOnQueueBlock(^)(id result))mkresolvedCallback
pending:(void(^)(id result, PMKPromise *next, dispatch_queue_t q, id block, void (^resolver)(id)))mkpendingCallback 这个函数
- mkresolvedCallback 当promise 有结果的时候被调用
2 mkpendingCallback 当promise 没有结果的时候被调用
接下来看
- (PMKPromise *(^)(id))catch;
- (PMKPromise *(^)(dispatch_queue_t, id))catchOn;
- (PMKResolveOnQueueBlock)catchOn {
return [self resolved:^(id result) {
if (IsPromise(result))
return ((PMKPromise *)result).catchOn;
if (IsError(result)) return ^(dispatch_queue_t q, id block) {
// HACK we seem to expose some bug in ARC where this block can
// be an NSStackBlock which then gets deallocated by the time
// we get around to using it. So we force it to be malloc'd.
block = [block copy];
return dispatch_promise_on(q, ^{
[PMKError consume:result];
return pmk_safely_call_block(block, result);
});
};
return ^(dispatch_queue_t q, id block) {
return [PMKPromise promiseWithValue:result];
};
}
pending:^(id result, PMKPromise *next, dispatch_queue_t q, id block, void (^resolve)(id)) {
if (IsError(result)) {
dispatch_async(q, ^{
[PMKError consume:result];
resolve(pmk_safely_call_block(block, result));
});
} else
PMKResolve(next, result);
}];
}
结构和thenOn 一样,只不过只是调用- (id)resolved:(PMKResolveOnQueueBlock(^)(id result))mkresolvedCallback
pending:(void(^)(id result, PMKPromise *next, dispatch_queue_t q, id block, void (^resolver)(id)))mkpendingCallback 这个函数 函数传入的block不一样而已
1 . mkresolvedCallback 样式一样,只不过和thenOn不同的是,对正常数据直接返回了,而对NSerror数据进行处理了,和thenOn正好相反
2 mkpendingCallback 处理方式相同
3 其实也可以这么理解,thenOn在promise的链条上只处理正确数据,对错误数据只沿着promise链条传递下去。而catchOn只处理错误数据,对正确的数据,对正确数据不处理,只沿着promise链条传递下去。
剩下一组,不管正确数据错误数据都处理的api了
- (PMKPromise *(^)(void(^)(void)))finally;
- (PMKPromise *(^)(dispatch_queue_t, void(^)(void)))finallyOn;
- (PMKPromise *(^)(dispatch_queue_t, dispatch_block_t))finallyOn {
return [self resolved:^(id passthru) {
if (IsPromise(passthru))
return ((PMKPromise *)passthru).finallyOn;
return ^(dispatch_queue_t q, dispatch_block_t block) {
// HACK we seem to expose some bug in ARC where this block can
// be an NSStackBlock which then gets deallocated by the time
// we get around to using it. So we force it to be malloc'd.
block = [block copy];
return dispatch_promise_on(q, ^{
block();
return passthru;
});
};
} pending:^(id passthru, PMKPromise *next, dispatch_queue_t q, dispatch_block_t block, void (^resolve)(id)) {
dispatch_async(q, ^{
@try {
block();
resolve(passthru);
} @catch (id e) {
resolve(NSErrorFromException(e));
}
});
}];
}
- 这个finallyOn 传入的block 只能是没有参数的block。
2 返回结果,不对结果做任何处理,。
这里主要的api 都解决了。看看其他的细节问题
+ (instancetype)promiseWithAdapter:(void (^)(PMKAdapter))block {
return [self promiseWithResolver:^(PMKResolver resolve) {
block(^(id value, id error){
resolve(error ?: value);
});
}];
}
这个 根据外面是否传入error 来判断结果,如果传入error那么我们就将error沿着promise,否则沿着正常数据传递
还有两个类似的方法
+ (instancetype)promiseWithIntegerAdapter:(void (^)(PMKIntegerAdapter))block {
return [self promiseWithResolver:^(PMKResolver resolve) {
block(^(NSInteger value, id error){
if (error) {
resolve(error);
} else {
resolve(@(value));
}
});
}];
}
+ (instancetype)promiseWithBooleanAdapter:(void (^)(PMKBooleanAdapter adapter))block {
return [self promiseWithResolver:^(PMKResolver resolve) {
block(^(BOOL value, id error){
if (error) {
resolve(error);
} else {
resolve(@(value));
}
});
}];
}
以上两个不做介绍,写个小例子,看看就行了
[PMKPromise promiseWithAdapter:^(PMKAdapter adapter) {
adapter(@"adapter",nil);
}].then(^(NSString *str){
NSLog(@"%@",str);
});
最多支持三个参数实现
我们知道我们在调用promise 的fulfiller 或者是rejecter 的时候传入的参数是一个,而在Then,Catch 的时候,我们传入的block可以支持最多不超过三个参数,这如何实现的呢?
其实fulfiller 或者是rejecter 传入的数据要是多个参数的时候,我们是需要将其打包成 PMKArray 数组的,这个数组其实就是current 的result了。在传递到next promise的时候,我们通过id pmk_safely_call_block(id frock, id result) ,解析出next Promise 传入的block的参数,根据参数个数,从 PMKArray数组中依次取出来。
特殊API
promiseKit 支持 when 和until 。when 就是当所有的任务执行完毕在执行我。我们看看如何实现的when。
+ (PMKPromise *)when:(id)promises {
if ([promises conformsToProtocol:@protocol(NSFastEnumeration)]) {
return [self all:promises];
} else if (promises) {
return [self all:@[promises]].then(^(NSArray *values){
return [values objectAtIndex:0];
});
} else {
return [PMKPromise promiseWithValue:nil];
}
}
+ (PMKPromise *)all:(id<NSFastEnumeration, NSObject>)promises {
__block NSUInteger count = [(id)promises count]; // FIXME
if (count == 0)
return [PMKPromise promiseWithValue:promises];
// Keep a reference to the newly created
// promise so we can check if it's resolved
// when one of the passed in promises fails.
__block PMKPromise *newPromise = nil;
#define rejecter(key) ^(NSError *err){ \
if (newPromise.resolved) \
return; \
NSMutableDictionary *userInfo = err.userInfo.mutableCopy; \
[userInfo setObject:key forKey:PMKFailingPromiseIndexKey]; \
err = [NSError errorWithDomain:err.domain code:err.code userInfo:userInfo]; \
rejecter(err); \
}
if ([promises isKindOfClass:[NSDictionary class]])
return newPromise = [PMKPromise new:^(PMKPromiseFulfiller fulfiller, PMKPromiseRejecter rejecter){
NSDictionary *promiseDictionary = (NSDictionary *) promises;
NSMutableDictionary *results = [NSMutableDictionary new];
for (id key in promiseDictionary) {
PMKPromise *promise = [promiseDictionary objectForKey:key];
if (![promise isKindOfClass:[PMKPromise class]])
promise = [PMKPromise promiseWithValue:promise];
promise.catch(rejecter(key));
promise.then(^(id o){
if (o)
[results setObject:o forKey:key];
if (--count == 0)
fulfiller(results);
});
}
}];
return newPromise = [PMKPromise new:^(PMKPromiseFulfiller fulfiller, PMKPromiseRejecter rejecter){
NSPointerArray *results = nil;
#if TARGET_OS_IPHONE
results = [NSPointerArray strongObjectsPointerArray];
#else
if ([[NSPointerArray class] respondsToSelector:@selector(strongObjectsPointerArray)]) {
results = [NSPointerArray strongObjectsPointerArray];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
results = [NSPointerArray pointerArrayWithStrongObjects];
#pragma clang diagnostic pop
}
#endif
results.count = count;
NSUInteger ii = 0;
for (__strong PMKPromise *promise in promises) {
if (![promise isKindOfClass:[PMKPromise class]])
promise = [PMKPromise promiseWithValue:promise];
promise.catch(rejecter(@(ii)));
promise.then(^(id o){
[results replacePointerAtIndex:ii withPointer:(__bridge void *)(o ?: [NSNull null])];
if (--count == 0)
fulfiller(results.allObjects);
});
ii++;
}
}];
#undef rejecter
}
第一个函数很简单不做介绍,主要看第二个函数
1 参数 是promises ,必须能枚举的集合吧。数组字典set都可以。
2 要是集合数量是0 ,那么直接返回一个PMKPromise,这里也是无奈啊,这个值不知道穿啥好,就把自己传回去了
- 要是promises 是个NSDictionary ,我们创建一个PMKPromise 命名为newPromise。我们知道这个newPromise 有两个状态回调block,分别是fulfiller 和 rejecter,通过这两个block能将结果传入newPromise 中,因此,这里我们就将在字典中的所有promise 都分别或者通过字典值生成的promise 分别持有fulfiller 和 rejecter,只要有一个promise 结果是NSerror ,那么我们就结束newPromise,让其保存NSError结果。所有promise要是没有错误,当每一个promise 结束的时候,将所有promise共享的计数器减1 ,当计数器变为0 的时候,也就是最后一个promise完成的时候,我们再将newPromise 的fulfiller 调用,让其newPromise 结束。数组同理。
说的不明确,看图就行了,关系图
image.png
这里还有个until 用法,我就写个简单代码体会下。
array = [NSMutableArray array];
PMKPromise * p = [PMKPromise promiseWithAdapter:^(PMKAdapter adapter) {
adapter(@"nihao",nil);
}];
[array addObject:p];
p = [PMKPromise promiseWithAdapter:^(PMKAdapter adapter) {
NSError * error = [NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:nil];;
adapter(@"error",error);
}];
[array addObject:p];
[array addObject:@"dd"];
[PMKPromise until:^id{
return array;
} catch:^(NSError * error){
NSDictionary * dic = [error userInfo];
NSNumber * num =[dic objectForKey:PMKFailingPromiseIndexKey];
[array removeObjectAtIndex:num.integerValue];
NSLog(@"%@",[error description]);
}].then(^(id result){
NSLog(@"%@",result);
});
运行结果
2018-08-21 16:21:45.645773+0800 OriginCodeAnalytical[98249:1362520] Error Domain=PMKErrorDomain Code=3 "(null)" UserInfo={PMKFailingPromiseIndexKey=1}
2018-08-21 16:21:45.647217+0800 OriginCodeAnalytical[98249:1362520] (
nihao,
dd
)
网友评论