iOS 如何优雅的处理“回调地狱Callback hell”(一) (下)

了解完流程以后,就能够开始继续研究源码了。在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先分享到这里。

 

若有错误,还请你们请多多指教。

相关文章
相关标签/搜索