最近在嘗試解決下載管理中,全部下載的速度問題,於是在github中試圖尋找答案,無意中發現了Promise這個第三方庫。它的作用如下
Future
is a sort of a placeholder object that you can create for a result that does not yet exist. Generally, the result of the
Future
is computed concurrently and can be later collected. Composing concurrent tasks in this way tends to result in faster, asynchronous, non-blocking parallel code.
@implementation MyViewController - (void)viewDidLoad { id rq = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://placekitten.com/320/320"]]; void (^handleError)(id) = ^(NSString *msg){ UIAlertView *alert = [[UIAlertView alloc] init… delegate:self …]; [alert show]; }; [NSURLConnection sendAsynchronousRequest:rq completionHandler:^(id response, id data, id error) { if (error) { handle([error localizedDescription]); } else { UIImage *image = [UIImage imageWithData:data]; if (!image) { handleError(@"Bad server response"); } else { self.imageView.image = image; } } }]; } - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { // hopefully we won’t ever have multiple UIAlertViews handled in this class [self dismissViewControllerAnimated:YES]; } @end
#import <PromiseKit.h> - (void)viewDidLoad { [NSURLConnection GET:@"http://placekitten.com/320/320"].then(^(UIImage *image) { self.imageView.image = image; }).catch(^(NSError *error){ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:…]; [alert promise].then(^{ [self dismissViewControllerAnimated:YES]; }) }); }

A `Promise` represents the future value of a task.
To obtain the value of a `Promise`, call `then`. When the asynchronous task that this `Promise` represents has resolved successfully, the block you pass to `then` will be executed with the resolved value. If the `Promise` has already been resolved succesfully at the time you `then` the `Promise`, the block will be executed immediately.
Effective use of Promises involves chaining `then`s, where the return value from one `then` is fed as the value of the next, et cetera.
promise 代表了一個任務的未來的值,
UIAlertView * alert=[[UIAlertView alloc] initWithTitle:@"title" message:@"message" delegate:nil cancelButtonTitle:@"sure" otherButtonTitles:nil]; [alert promise].then(^(){ NSLog(@"excute!"); });
在創建 UI|AlertView之后調用它的promise對象,然后調用then就可以了。調用方法相當簡單,具體使用可以參考:http://promisekit.org。
- (PMKPromise *)promise { PMKAlertViewDelegater *d = [PMKAlertViewDelegater new]; PMKRetain(d); self.delegate = d; [self show]; return [PMKPromise new:^(id fulfiller, id rejecter){ d->fulfiller = fulfiller; }]; }
它的promise方法很簡單,僅僅就是創建並返回了一個PMKPromise對象,繼續來看看promise的then方法,這個是最主要的方法,如果這個方法理解清楚了,這個框架就可以舉一反三的大致理解清楚了。
- (PMKPromise *(^)(id))then { return ^(id block){ return self.thenOn(dispatch_get_main_queue(), block); }; }
我們看到then 他會調用thenOn方法,都返回的是一個block塊。
- (PMKPromise *(^)(id))then 函數和調用形式[alert promise].then(^(){ NSLog(@"excute!");}) ,我們自己看看是不是感覺很奇怪,為什么then后面竟然帶的是中括號,而我們通常在使用block的時候,一般都是帶{}進行操作的,想想 [UIView animate:^{ }] ,是不是完全不一樣,這里就涉及了一個block的用法了,就我來說看的和用的還是比較少的。區別如下:
1.通常,我們例如[UIView animate:^{ }] 都是在文件內部進行block的調用,而在客戶端(調用的時候)來定義block塊,我們通常用block塊替換delegate的時候就是這么做得,也是大眾的做法
2.此處,剛剛是反過來的,在內部定義block塊,在外部調用,這樣做得好處是在調用的時候就直接能執行。(想想promise是異步操作的一個庫哦)
- (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 safely_call_block(block, result); }); }; } pending:^(id result, PMKPromise *next, dispatch_queue_t q, id block, PMKPromiseFulfiller resolve) { if (IsError(result)) return PMKResolve(next, result); dispatch_async(q, ^{ resolve(safely_call_block(block, result)); }); }]; }
thenOn方法又調用了- (id)resolved:(PMKResolveOnQueueBlock(^)(id result))mkresolvedCallback pending:(void(^)(id result, PMKPromise *next, dispatch_queue_t q, id block, PMKPromiseFulfiller resolver))mkpendingCallback 函數,一層層的調用,一層還沒理解就下一次,無窮無盡了呢。不過沒辦法,只能接着往下看了。
- (id)resolved:(PMKResolveOnQueueBlock(^)(id result))mkresolvedCallback pending:(void(^)(id result, PMKPromise *next, dispatch_queue_t q, id block, PMKPromiseFulfiller resolver))mkpendingCallback { __block PMKResolveOnQueueBlock callBlock; __block id result; dispatch_sync(_promiseQueue, ^{ if ((result = _result)) return; callBlock = ^(dispatch_queue_t q, id block) { __block PMKPromise *next = nil; // 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]; 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); }
我們看到dispatch_barrier_sync函數,等待該隊列前面的操作任務執行完成后才會執行這個block塊。這至關重要的函數保證了then的同步執行,它的block塊作用如下,創建了一個新的PMKPromise並且把then中帶的block添加到_handlers, 這兩個操作是關鍵步驟,至於這個函數中得大多其他代碼都是進行遞歸解析then方法(別忘了,這是一個鏈式調用方法)。
resolver = ^(id o){ if (IsError(o)) reject(o); else fulfill(o); };
這里我們需要最后跟進一步,看看new 方法中得fulfill是什么:
id fulfiller = ^(id value){ if (PMKGetResult(this)) return NSLog(@"PromiseKit: Promise already resolved"); if (IsError(value)) @throw PMKE(@"You may not fulfill a Promise with an NSError"); PMKResolve(this, value); };
調用了PMKResolve函數,其他都是一些異常的處理。最后看看這個函數
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); }
執行所有的handler操作,並且設置result值。這里我們是否記得,在上面我們有把block存到handler中得。於是block就順利的執行了。