iOS如何優雅的處理「回調地獄Callback hell」(一)——使用PromiseKit

前言

最近看了一些Swift關於封裝異步操做過程的文章,好比RxSwift,RAC等等,由於回調地獄我本身也寫過,頗有感觸,因而就翻出了Promise來研究學習一下。現將本身的一些收穫分享一下,有錯誤歡迎你們多多指教。javascript

目錄

  • 1.PromiseKit簡介
  • 2.PromiseKit安裝和使用
  • 3.PromiseKit主要函數的使用方法
  • 4.PromiseKit的源碼解析
  • 5.使用PromiseKit優雅的處理回調地獄

一.PromiseKit簡介

PromiseKit是iOS/OS X 中一個用來出來異步編程框架。這個框架是由Max Howell(Mac下Homebrew的做者,傳說中由於"不會"寫反轉二叉樹而沒有拿到Google offer)大神級人物開發出來的。java

在PromiseKit中,最重要的一個概念就是Promise的概念,Promise是異步操做後的future的一個值。ios

A promise represents the future value of an asynchronous task.
A promise is an object that wraps an asynchronous task編程

Promise也是一個包裝着異步操做的一個對象。使用PromiseKit,可以編寫出整潔,有序的代碼,邏輯簡單的,將Promise做爲參數,模塊化的從一個異步任務到下一個異步任務中去。用PromiseKit寫出的代碼就是這樣:json

[self login].then(^{

     // our login method wrapped an async task in a promise
     return [API fetchData];

}).then(^(NSArray *fetchedData){

     // our API class wraps our API and returns promises
     // fetchedData returned a promise that resolves with an array of data
     self.datasource = fetchedData;
     [self.tableView reloadData];

}).catch(^(NSError *error){

     // any errors in any of the above promises land here
     [[[UIAlertView alloc] init…] show];

});複製代碼

PromiseKit就是用來乾淨簡潔的代碼,來解決異步操做,和奇怪的錯誤處理回調的。它將異步操做變成了鏈式的調用,簡單的錯誤處理方式。swift

PromiseKit裏面目前有2個類,一個是Promise (Swift),一個是AnyPromise(Objective-C),2者的區別就在2種語言的特性上,Promise 是定義精確嚴格的,AnyPromise是定義寬鬆,靈活,動態的。 api

在異步編程中,有一個最最典型的例子就是回調地獄CallBack hell,要是處理的不優雅,就會出現下圖這樣:
數組


上圖的代碼是真實存在的,也是朋友告訴個人,來自 快的的代碼,固然如今人家確定改掉了。雖然這種代碼看着像這樣:


代碼雖然看上去不優雅,功能都是正確的,可是這種代碼基本你們都本身寫過,我本身也寫過不少。今天就讓咱們動起手來,用PromiseKit來優雅的處理掉Callback hell吧。

二.PromiseKit安裝和使用

1.下載安裝CocoaPods promise

在牆外的安裝步驟:
在Terminal裏面輸入ruby

sudo gem install cocoapods && pod setup複製代碼

大多數在牆內的同窗應該看以下步驟了:

//移除原有的牆外Ruby 默認源
$ gem sources --remove https://rubygems.org/
//添加現有的牆內的淘寶源
$ gem sources -a https://ruby.taobao.org/
//驗證新源是否替換成功
$ gem sources -l
//下載安裝cocoapods
// OS 10.11以前
$ sudo gem install cocoapods
//mark:OS 升級 OS X EL Capitan 後命令應該爲:
$ sudo gem install -n /usr/local/bin cocoapods
//設置cocoapods
$ pod setup複製代碼

2.找到項目的路徑,進入項目文件夾下面,執行:

$ touch Podfile && open -e Podfile複製代碼

此時會打開TextEdit,而後輸入一下命令:

platform:ios, ‘7.0’

target 'PromisekitDemo' do  //因爲最新版cocoapods的要求,因此必須加入這句話
    pod 'PromiseKit'
end複製代碼

Tips:感謝qinfensky大神提醒,其實這裏也能夠用init命令
Podfile是CocoaPods的特殊文件,在其中能夠列入在項目中想要使用的開源庫,若想建立Podfile,有2種方法:
1.在項目目錄中建立空文本文件,命名爲Podfile
2.或者能夠再項目目錄中運行「$ pod init 「,來建立功能性文件(終端中輸入cd 文件夾地址,而後再輸入 pod init)
兩種方法均可以建立Podfile,使用你最喜歡使用的方法

3.安裝PromiseKit

$ pod install複製代碼

安裝完成以後,退出終端,打開新生成的.xcworkspace文件便可

三.PromiseKit主要函數的使用方法

  1. then
    常常咱們會寫出這樣的代碼:
- (void)showUndoRedoAlert:(UndoRedoState *)state
{
     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:……];
     alert.delegate = self; 
     self.state = state;
     [alert show];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1) {
        [self.state do];
    }

}複製代碼

上面的寫法也不是錯誤的,就是它在調用函數中保存了一個屬性,在調用alertView會使用到這個屬性。其實這個中間屬性是不須要存儲的。接下來咱們就用then來去掉這個中間變量。

- (void)showUndoRedoAlert:(UndoRedoState *)state
 {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:……];
    [alert promise].then(^(NSNumber *dismissedButtonIndex){
        [state do];
    });
}複製代碼

這時就有人問了,爲啥能調用[alert promise]這個方法?後面點語法跟着then是什麼?我來解釋一下,緣由其實只要打開Promise源碼就一清二楚了。在pormise源碼中

@interface UIAlertView (PromiseKit)

/** Displays the alert view. @return A promise the fulfills with two parameters: 1) The index of the button that was tapped to dismiss the alert. 2) This alert view. */
- (PMKPromise *)promise;複製代碼

對應的實現是這樣的

- (PMKPromise *)promise {
    PMKAlertViewDelegater *d = [PMKAlertViewDelegater new];
    PMKRetain(d);
    self.delegate = d;
    [self show];
    return [PMKPromise new:^(id fulfiller, id rejecter){
        d->fulfiller = fulfiller;
    }];
}複製代碼

調用[alert promise]返回仍是一個promise對象,在promise的方法中有then的方法,因此上面能夠那樣鏈式的調用。上面代碼裏面的fulfiller放在源碼分析裏面去講講。

在PromiseKit裏面,其實就默認給你建立了幾個類的延展,以下圖


這些擴展類裏面就封裝了一些經常使用的生成promise方法,調用這些方法就能夠愉快的一路.then執行下去了!

2.dispatch_promise
項目中咱們常常會異步的下載圖片

typedefvoid(^onImageReady) (UIImage* image);

+ (void)getImageWithURL:(NSURL *)url onCallback:(onImageReady)callback
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
    dispatch_async(queue, ^{
        NSData * imageData = [NSData dataWithContentsOfURL:url];
        dispatch_async(dispatch_get_main_queue(), ^{
            UIImage *image = [UIImage imageWithData:imageData];
            callback(image);
        });
    });
}複製代碼

使用dispatch_promise,咱們能夠將它改變成下面這樣:

dispatch_promise(^{
        return [NSData dataWithContentsOfURL:url];     
    }).then(^(NSData * imageData){ 
        self.imageView.image = [UIImage imageWithData:imageData];  
    }).then(^{
        // add code to happen next here
    });複製代碼

咱們看看源碼,看看調用的異步過程對不對

- (PMKPromise *(^)(id))then {
    return ^(id block){
        return self.thenOn(dispatch_get_main_queue(), block);
    };
}

PMKPromise *dispatch_promise(id block) {
    return dispatch_promise_on(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}複製代碼

看了源碼就知道上述是正確的。

3.catch
在異步操做中,處理錯誤也是一件很頭疼的事情,以下面這段代碼,每次異步請求回來都必需要處理錯誤。

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[@"avatar_url"]]];
            [NSURLConnection sendAsynchronousRequest:rq queue:q completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                UIImage *image = [UIImage imageWithData:data];
                if (!image) {
                    errorHandler(nil); // NSError TODO!
                } else {
                    self.imageView.image = image;
                }
            }];
        }
    }
}];複製代碼

咱們能夠用promise的catch來解決上面的錯誤處理的問題

//oc版
[NSURLSession GET:url].then(^(NSDictionary *json){
    return [NSURLConnection GET:json[@"avatar_url"]];
}).then(^(UIImage *image){
    self.imageView.image = image;
}).catch(^(NSError *error){
    [[UIAlertView …] show];
})複製代碼
//swift版
firstly {
    NSURLSession.GET(url)
}.then { (json: NSDictionary) in
    NSURLConnection.GET(json["avatar_url"])
}.then { (image: UIImage) in
    self.imageView.image = image
}.error { error in
    UIAlertView(…).show()
}複製代碼

用了catch之後,在傳遞promise的鏈中,一旦中間任何一環產生了錯誤,都會傳遞到catch去執行Error Handler。

4.when
一般咱們有這種需求:
在執行一個A任務以前還有1,2個異步的任務,在所有異步操做完成以前,須要阻塞A任務。代碼可能會寫的像下面這樣子:

__block int x = 0;
void (^completionHandler)(id, id) = ^(MKLocalSearchResponse *response, NSError *error){
    if (++x == 2) {
        [self finish];
    }
};
[[[MKLocalSearch alloc] initWithRequest:rq1] startWithCompletionHandler:completionHandler];
[[[MKLocalSearch alloc] initWithRequest:rq2] startWithCompletionHandler:completionHandler];複製代碼

這裏就可使用when來優雅的處理這種狀況:

id search1 = [[[MKLocalSearch alloc] initWithRequest:rq1] promise];
id search2 = [[[MKLocalSearch alloc] initWithRequest:rq2] promise];

PMKWhen(@[search1, search2]).then(^(NSArray *results){
    //…
}).catch(^{
    // called if either search fails
});複製代碼

在when後面傳入一個數組,裏面是2個promise,只有當這2個promise都執行完,纔會去執行後面的then的操做。這樣就達到了以前所說的需求。

這裏when還有2點要說的,when的參數還能夠是字典。

id coffeeSearch = [[MKLocalSearch alloc] initWithRequest:rq1];
id beerSearch = [[MKLocalSearch alloc] initWithRequest:rq2];
id input = @{@"coffee": coffeeSearch, @"beer": beerSearch};

PMKWhen(input).then(^(NSDictionary *results){
    id coffeeResults = results[@"coffee"];
});複製代碼

這個例子裏面when傳入了一個input字典,處理完成以後依舊能夠生成新的promise傳遞到下一個then中,在then中能夠去到results的字典,得到結果。傳入字典的工做原理放在第四章會解釋。

when傳入的參數還能夠是一個可變的屬性:

@property id dataSource;

- (id)dataSource {
    return dataSource ?: [PMKPromise new:…];
}

- (void)viewDidAppear {
    [PMKPromise when:self.dataSource].then(^(id result){
        // cache the result
        self.dataSource = result;
    });
}複製代碼

dataSource若是爲空就新建一個promise,傳入到when中,執行完以後,在then中拿到result,並把result賦值給dataSource,這樣dataSource就有數據了。由此看來,when的使用很是靈活!

5.always & finally

//oc版
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self myPromise].then(^{
    //…
}).finally(^{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
})複製代碼
//swift版
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
myPromise().then {
    //…
}.always {
    UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}複製代碼

在咱們執行完then,處理完error以後,還有一些操做,那麼就能夠放到finally和always裏面去執行。

四.PromiseKit的源碼解析

通過上面對promise的方法的學習,咱們已經能夠了解到,在異步操做咱們能夠經過不斷的返回promise,傳遞給後面的then來造成鏈式調用,因此重點就在then的實現了。在討論then以前,我先說一下promise的狀態和傳遞機制。

一個promise可能有三種狀態:等待(pending)、已完成(fulfilled)、已拒絕(rejected)。
一個promise的狀態只可能從「等待」轉到「完成」態或者「拒絕」態,不能逆向轉換,同時「完成」態和「拒絕」態不能相互轉換。
promise必須實現then方法(能夠說,then就是promise的核心),並且then必須返回一個promise,同一個promise的then能夠調用屢次,而且回調的執行順序跟它們被定義時的順序一致
then方法接受兩個參數,第一個參數是成功時的回調,在promise由「等待」態轉換到「完成」態時調用,另外一個是失敗時的回調,在promise由「等待」態轉換到「拒絕」態時調用。同時,then能夠接受另外一個promise傳入,也接受一個「類then」的對象或方法,即thenable對象


總結起來就是上圖,pending狀態的promise對象既可轉換爲帶着一個成功值的 fulfilled 狀態,也可變爲帶着一個 error 信息的 rejected 狀態。當狀態發生轉換時, promise.then 綁定的方法就會被調用。(當綁定方法時,若是 promise 對象已經處於 fulfilled 或 rejected 狀態,那麼相應的方法將會被馬上調用, 因此在異步操做的完成狀況和它的綁定方法之間不存在競爭關係。)從Pending轉換爲fulfilled或Rejected以後, 這個promise對象的狀態就不會再發生任何變化。所以 then是隻被調用一次的函數,從而也能說明,then生成的是一個新的promise,而不是原來的那個。

瞭解完流程以後,就能夠開始繼續研究源碼了。在PromiseKit當中,最經常使用的當屬then,thenInBackground,catch,finally

- (PMKPromise *(^)(id))then {
    return ^(id block){
        return self.thenOn(dispatch_get_main_queue(), block);
    };
}

- (PMKPromise *(^)(id))thenInBackground {
    return ^(id block){
        return self.thenOn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
    };
}

- (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先分享到這裏。

若有錯誤,還請你們請多多指教。

相關文章
相關標籤/搜索