瞭解完流程以後,就能夠開始繼續研究源碼了。在PromiseKit當中,最經常使用的當屬then,thenInBackground,catch,finallyjson
- (PMKPromise *(^)(id))then {數組
return ^(id block){promise
return self.thenOn(dispatch_get_main_queue(), block);網絡
};框架
}異步
- (PMKPromise *(^)(id))thenInBackground {async
return ^(id block){函數
return self.thenOn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);學習
};this
}
- (PMKPromise *(^)(id))catch {
return ^(id block){
return self.catchOn(dispatch_get_main_queue(), block);
};
}
- (PMKPromise *(^)(dispatch_block_t))finally {
return ^(dispatch_block_t block) {
return self.finallyOn(dispatch_get_main_queue(), block);
};
}
這四個方法底層調用了各自的thenon,catchon,finallyon方法,這些on的方法實現基本都差很少,那我就以最重要的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) {
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));
});
}];
}
這個thenon就是返回一個方法,因此繼續往下看
- (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) {
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);
}];
});
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);
}
這個方法看上去很複雜,仔細看看,函數的形參其實就是2個block,一個是resolved的block,還有一個是pending的block。當一個promise經歷過resolved以後,多是fulfill,也多是reject,以後生成next新的promise,傳入到下一個then中,而且狀態會變成pending。上面代碼中第一個return,若是next爲nil,那麼意味着promise沒有生成,這是會再調用一次mkresolvedCallback,並傳入參數result,生成的PMKResolveOnQueueBlock,再次傳入(q, block),直到next的promise生成,並把pendingCallback存入到handler當中。這個handler存了全部待執行的block,若是把這個數組裏面的block都執行,那麼就至關於依次完成了上面的全部異步操做。第二個return是在callblock爲nil的時候,還會再調一次mkresolvedCallback(result),保證必定要生成next的promise。
這個函數裏面的這裏dispatch_barrier_sync這個方法,就是promise後面能夠鏈式調用then的緣由,由於GCD的這個方法,讓後面then變得像一行行的then順序執行了。
可能會有人問了,並無看到各個block執行,僅僅只是加到handler數組裏了,這個問題的答案,就是promise的核心了。promise執行block的操做是放在resove裏面的。先來看看源碼
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);
}
這是一個遞歸函數,能造成遞歸的條件就是那句PMKResolve(this, o);當nextResult = nil的時候,就表明了這個promise仍是pending狀態,尚未被執行,這個時候就要遞歸調用,直到nextResult不爲nil。不爲nil,就會調用set方法,set方法是一個匿名函數,裏面的for循環會依次循環,執行handler數組裏面的每個block。裏面的那個if語句,是先判斷result是不是一個promise,若是不是promise,就去執行set方法,依次調用各個block。
至此,一個then的執行原理就到此結束了。接下來咱們再看看when的原理。
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++;
}
}];
這裏只截取了return的部分,理解了then,這裏再看when就好理解了。when就是在傳入的promises的數組裏面,依次執行各個promise,結果最後傳給新生成的一個promise,做爲返回值返回。
這裏要額外提一點的就是若是給when傳入一個字典,它會如何處理的
if ([promises isKindOfClass:[NSDictionary class]])
return newPromise = [PMKPromise new:^(PMKPromiseFulfiller fulfiller, PMKPromiseRejecter rejecter){
NSMutableDictionary *results = [NSMutableDictionary new];
for (id key in promises) {
PMKPromise *promise = promises[key];
if (![promise isKindOfClass:[PMKPromise class]])
promise = [PMKPromise promiseWithValue:promise];
promise.catch(rejecter(key));
promise.then(^(id o){
if (o)
results[key] = o;
if (--count == 0)
fulfiller(results);
});
}
}];
方式和when的數組方式基本同樣,只不過多了一步,就是從字典裏面先取出promise[key],而後再繼續對這個promise執行操做而已。因此when能夠傳入以promise爲value的字典。
五.使用PromiseKit優雅的處理回調地獄
這裏我就舉個例子,你們一塊兒來感覺感覺用promise的簡潔。
先描述一下環境,假設有這樣一個提交按鈕,當你點擊以後,就會提交一次任務。首先要先判斷是否有權限提交,沒有權限就彈出錯誤。有權限提交以後,還要請求一次,判斷當前任務是否已經存在,若是存在,彈出錯誤。若是不存在,這個時候就能夠安心提交任務了。
void (^errorHandler)(NSError *) = ^(NSError *error) {
[[UIAlertView …] show];
};
[NSURLConnection sendAsynchronousRequest:rq queue:q completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
errorHandler(connectionError);
} else {
NSError *jsonError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError) {
errorHandler(jsonError);
} else {
id rq = [NSURLRequest requestWithURL:[NSURL URLWithString:json[@"have_authority"]]];
[NSURLConnection sendAsynchronousRequest:rq queue:q completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSError *jsonError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError) {
errorHandler(jsonError);
} else {
id rq = [NSURLRequest requestWithURL:[NSURL URLWithString:json[@"exist"]]];
[NSURLConnection sendAsynchronousRequest:rq queue:q completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSError *jsonError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError) {
errorHandler(jsonError);
} else {
if ([json[@"status"] isEqualToString:@"OK"]) {
[self submitTask];
} else {
errorHandler(json[@"status"]);
}
}
}];
}
}];
}
}
}];
上面的代碼裏面有3層回調,看上去就很暈,接下來咱們用promise來整理一下。
[NSURLSession GET:url].then(^(NSDictionary *json){
return [NSURLConnection GET:json[@"have_authority"]];
}).then(^(NSDictionary *json){
return [NSURLConnection GET:json[@"exist"]];
}).then(^(NSDictionary *json){
if ([json[@"status"] isEqualToString:@"OK"]) {
return [NSURLConnection GET:submitJson];
} else
@throw [NSError errorWithDomain:… code:… userInfo:json[@"status"]];
}).catch(^(NSError *error){
[[UIAlertView …] show];
})
以前將近40行代碼就一會兒變成15行左右,看上去比原來清爽多了,可讀性更高。
最後
看完上面關於PromiseKit的使用方法以後,其實對於PromiseKit,我我的的理解它就是一個Monad(這是最近很火的一個概念,4月底在上海SwiftCon 2016中,唐巧大神分享的主題就是關於Monad,還不是很瞭解這個概念的能夠去他博客看看,或者找視頻學習學習。)Promise就是一個盒子裏面封裝了一堆操做,then對應的就是一組flatmap或map操做。不過缺點也仍是有,若是網絡用的AFNetWorking,網絡請求頗有可能會回調屢次,這時用PromiseKit,就須要本身封裝一個屬於本身的promise了。PromiseKit原生的是用的OMGHTTPURLRQ這個網絡框架。PromiseKit裏面自帶的封裝的網絡請求也仍是基於NSURLConnection的。因此用了AFNetWorking的同窗,要想再優雅的處理掉網絡請求引發的回調地獄的時候,本身仍是須要先封裝一個本身的Promise,而後優雅的then一下。不少人可能看到這裏,以爲我引入一個框架,原本是來解決問題的,可是如今還須要我再次封裝才能解決問題,有點不值得。
我本身的見解是,PromiseKit是個解決異步問題很優秀的一個開源庫,尤爲是解決回調嵌套,回調地獄的問題,效果很是明顯。雖然須要本身封裝AFNetWorking的promise,可是它的思想很是值得咱們學習的!這也是接下來第二篇想和你們一塊兒分享的內容,利用promise的思想,本身來優雅的處理回調地獄!這一篇PromiseKit先分享到這裏。
若有錯誤,還請你們請多多指教。