上次说写了个顺序执行异步任务的,但有些情况并不适用,比如用登录后的token获取一些登录后没有返回的用户信息,再用这些信息请求对应的内容显示,这里有三次网络请求,依赖上一次的请求状态。显然上次写的那坨代码不能满足这个需求。
具体问题
有个名词定义上述的情况,回调地狱,给一张传说中以前的滴滴js代码截图
真特么人才,真心的。
其实我之前的做法也不比这个好到哪里去,当遇到网络回调嵌套的时候,就把嵌套的代码抽成方法,调用即可,这样无论有多少网络回调依赖,代码看起来没有回调地狱那么夸张了,但是这样会造成什么问题呢?
代码易读性并没有提高多少,拿上面登录那个举例,从调用上看,调用登录那一块代码,后面的获取用户信息和获取显示内容都会接着调用,别人需要到具体的方法回调里跟着一行一行看才知道是这么回事。除此之外,想要只登录,或者只获取用户信息,是没法重用这些方法的。
PromiseKit
如果只是为了解决回调地狱这个问题,PromiseKit足以胜任。
一般调用
本来,我们一般用block执行异步回调,方法类似下面这样
1 2 3 4 5 6 7 8 9 10 11 12
| - (void)sleep:(int)i success:(void (^)(void))sucess failed:(void (^)(void))failed{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(arc4random()%2+1); NSLog(@"%d", i); if (i==4) { failed(); }else { sucess(); } }); }
|
嵌套调用如下
1 2 3 4 5 6 7 8 9 10 11 12 13
| [self sleep:1 success:^{ [self sleep:2 success:^{ [self sleep:3 success:^{ NSLog(@"sucess"); } failed:^{ NSLog(@"failed"); }]; } failed:^{ NSLog(@"failed"); }]; } failed:^{ NSLog(@"failed"); }];
|
输出
这几乎是最简单的嵌套调用表达,因为没有回调逻辑没有条件语句,想象一下,里面如果再有标记判断,switch之类的,会是何等壮观。
使用PromiseKit
代码如下
异步封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| - (AnyPromise *)sleep:(int)i { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(arc4random()%2+1); NSLog(@"%d", i); if (i==4) { NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:100 userInfo:@{@"hhe":@"123"}]; resolve(error); }else { resolve(); }
}); }]; }
|
调用
1 2 3 4 5 6 7 8 9 10 11 12 13
| [self sleep:0].then(^(){ return [self sleep:1]; }).then(^(){ return [self sleep:2]; }).then(^(){ return [self sleep:3]; }).then(^(){ return [self sleep:4]; }).catch(^(){ NSLog(@"报错了"); }).always(^() { NSLog(@"运行结束"); });
|
for 循环调用
1 2 3 4 5 6 7 8 9 10 11 12 13
| AnyPromise *promise = [self sleep:0]; for (int i=1; i<6; i++) { promise = promise.then(^(){ return [self sleep:1]; }); if(i==5) { promise.catch(^(){ NSLog(@"报错了"); }).always(^() { NSLog(@"运行结束"); }); } }
|
补充
网上其它文章介绍所说的PMKPromiseFulfiller、PMKPromiseRejecter这两个block已经没有了,统一用PMKResolver处理,当参数为NSError或其子类时,就会由catch来处理,且剩余的promise不会继续执行。