开源库资料
源码地址
接触过Web前端开发的同学一定知道Promise这个东西,Promise其实就是一个封装着异步操作的一个对象,它可以通过resolve以及reject来控制整个分支的流程。个人理解这个就相当于一个没有办法立即给予答复的承诺,只知道这个地方会在恰当的时机会得到一个答复,至于什么时候得到怎样的答复,新建promise的时候并不确定。
下面是官方给出的一个定义:
A promise represents the future value of a task.
每个Promises都有一个状态,新建的每个Promises都处于pending状态,而后会转到resolve状态,resolve状态可以是fulfilled 或者 rejected ,如果是rejected 则会收到一个NSError,如果是fulfilled将会收到任何形式的对象。这个具体放在下面介绍。
下面的使用是基于PromiseKit 6.11.0 版本,可以通过下面的pod 方式导入:
use_frameworks! pod 'PromiseKit', '~> 6.11.0'
|
一个简单的例子:
[self promise_registerWithName:@"linxiaohai"].then(^(NSString *userName){ NSLog(@"Regist Successfull %@ !!!",userName); return [self promise_loginWithName:userName]; }).then(^(NSString *userName) { NSLog(@"Login Successfull %@ !!!",userName); }).catch(^(){ NSLog(@"Login Failed !!!"); }).finally(^(){ NSLog(@"Finally !!!"); });
- (PMKPromise *)promise_registerWithName:(NSString *)userName { return [PMKPromise promiseWithAdapter:^(PMKAdapter adapter) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ adapter(userName,nil); }); }]; }
- (PMKPromise *)promise_loginWithName:(NSString *)userName { return [PMKPromise promiseWithAdapter:^(PMKAdapter adapter) { adapter(userName,nil); }]; }
|
如果我们没有使用PromiseKit那么我们会在注册成功的block里面嵌套调用登录的请求,如果一个流程只是这两步还可以容忍,但是如果超过三步整个代码就会显得非常乱。因此如果在实际项目中遇到一个请求依赖上一个请求成功后才发起请求,也就是存在链式请求的话就可以考虑使用PromiseKit来规避Callback Hell.
PromiseKit的创建:
+ (instancetype)promiseWithValue:(id)value
|
将某个值封装成Promise返回
[AnyPromise promiseWithValue:@"linxiaohai"].then(^(id value){ NSLog(@"%@",value); });
|
+ (instancetype)promiseWithResolverBlock:(void (^)(PMKResolver _Nonnull))resolveBlock
|
当我们想将我们的非Promise异步代码添加到Promise链中的时候就可以使用上面这种方式来封装(需要注意的是这个方法仅仅适用于非Promise异步代码)这里的PMKResolver block参数是id类型,如果传入的是非NSError类型的话那么就会走到then流程,如果是NSError类型的话就会走catch流程。经过封装后的异步代码就可以使用then来链式调用了。下面是一个非常简单的一个例子:
[AnyPromise promiseWithResolverBlock:^(PMKResolver _Nonnull resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ resolver(@"linxiaohai"); }); }].then(^(id value){ NSLog(@"%@",value); });
|
下面是使用PMKAdapter来包装异步请求
+ (instancetype)promiseWithAdapter:(void (^)(PMKAdapter adapter))block; + (instancetype)promiseWithIntegerAdapter:(void (^)(PMKIntegerAdapter adapter))block; + (instancetype)promiseWithBooleanAdapter:(void (^)(PMKBooleanAdapter adapter))block;
|
PMKAdapter Block有两个参数,第一个参数是id类型,第二个参数是NSError类型:
typedef void (^PMKAdapter)(id, NSError *);
|
具体走哪个流程取决于两个值的情况,可以参照下表:
下面是一个例子:
[AnyPromise promiseWithAdapterBlock:^(PMKAdapter _Nonnull adapter) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ adapter(@"linxiaohai",[NSError errorWithDomain:@"test" code:100 userInfo:nil]); }); }].then(^(id value){ NSLog(@"%@",value); }).catch(^(NSError *error){ NSLog(@"%@",error); });
|
PMKWhen:
PMKWhen的参数是一个Promise数组,或者字典,它会等待所有的Promise执行结束,或者有一个Error发生,也就是说如果这些异步的Promise都没有Error的时候,会等到都Resolve后才执行then,并且then Block 传递回来的是各个Promise执行的结果,如果中途有Error发生就会中断,并且走到catch流程。下面是一个简单例子:
- (void)onCreate { [super onCreate]; PMKWhen(@[[self promise_delay_1s],[self promise_delay_error],[self promise_delay_3s],[self promise_delay_8s]]).then(^(id value){ NSLog(@"PMKWhen %@",value); }).catch(^(NSError *error){ NSLog(@"PMKWhen %@",error); }); }
- (AnyPromise *)promise_delay_1s { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_1s"); resolver(@"PMKWhen promise_delay_1s"); }); }]; }
- (AnyPromise *)promise_delay_3s { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_3s"); resolver(@"PMKWhen promise_delay_3s"); }); }]; }
- (AnyPromise *)promise_delay_8s { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_8s"); resolver(@"PMKWhen promise_delay_3s"); }); }]; }
- (AnyPromise *)promise_delay_error { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_error"); resolver([NSError errorWithDomain:@"test" code:100 userInfo:nil]); }); }]; }
|
输出结果如下:
2019-11-04 14:19:46.896521+0800 IDLFundationTest[91339:11794858] PMKWhen promise_delay_1s 2019-11-04 14:19:48.994048+0800 IDLFundationTest[91339:11794858] PMKWhen promise_delay_3s 2019-11-04 14:19:52.477392+0800 IDLFundationTest[91339:11794858] PMKWhen promise_delay_error 2019-11-04 14:19:52.477859+0800 IDLFundationTest[91339:11794858] PMKWhen Error Domain=test Code=100 "(null)" UserInfo={NSUnderlyingError=0x600000c64450 {Error Domain=test Code=100 "(null)"}, PMKFailingPromiseIndexKey=0} 2019-11-04 14:19:54.694791+0800 IDLFundationTest[91339:11794858] PMKWhen promise_delay_8s
|
这里需要注意的是6s发生错误之后走catch分支,之后就不再走then分支了,但是error发生之后promise_delay_8s还会继续进行,并不会中止。
PMKJoin
这个和PMKWhen有类似的地方,就是接受的参数是字典或者数组,但是它会等待所有的Promise都解决后才走then或者catch分支,不像PMKWhen那样一旦有错误发生就catch,下面是一个例子可以对比下:
- (void)onCreate { [super onCreate]; PMKJoin(@[[self promise_delay_1s],[self promise_delay_error],[self promise_delay_3s],[self promise_delay_8s]]).then(^(id value){ NSLog(@"PMKWhen %@",value); }).catch(^(NSError *error){ NSLog(@"PMKWhen %@",error); }); }
- (AnyPromise *)promise_delay_1s { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_1s"); resolver(@"PMKWhen promise_delay_1s"); }); }]; }
- (AnyPromise *)promise_delay_3s { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_3s"); resolver(@"PMKWhen promise_delay_3s"); }); }]; }
- (AnyPromise *)promise_delay_8s { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_8s"); resolver(@"PMKWhen promise_delay_8s"); }); }]; }
- (AnyPromise *)promise_delay_error { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_error"); resolver([NSError errorWithDomain:@"test" code:100 userInfo:nil]); }); }]; }
|
下面是执行的结果:
2019-11-04 14:23:53.419624+0800 IDLFundationTest[94348:11805962] PMKWhen promise_delay_1s 2019-11-04 14:23:55.419830+0800 IDLFundationTest[94348:11805962] PMKWhen promise_delay_3s 2019-11-04 14:23:59.018698+0800 IDLFundationTest[94348:11805962] PMKWhen promise_delay_error 2019-11-04 14:24:01.218821+0800 IDLFundationTest[94348:11805962] PMKWhen promise_delay_8s 2019-11-04 14:24:01.221100+0800 IDLFundationTest[94348:11805962] PMKWhen Error Domain=PMKErrorDomain Code=10 "(null)" UserInfo={PMKJoinPromisesKey=( "AnyPromise(PMKWhen promise_delay_1s)", "AnyPromise(PMKWhen promise_delay_3s)", "AnyPromise(PMKWhen promise_delay_8s)"
|
可以看出catch是在全部Promise完成后执行的,并且在UserInfo中可以看出哪些是成功的Promise.
PMKRace
PMKRace 会在第一个resolve(不论是fullfill还是reject)的时候执行then或者catch。
- (void)onCreate { [super onCreate]; PMKRace(@[[self promise_delay_1s],[self promise_delay_error],[self promise_delay_3s],[self promise_delay_8s]]).then(^(id value){ NSLog(@"PMKWhen %@",value); }).catch(^(NSError *error){ NSLog(@"PMKWhen %@",error); }); }
- (AnyPromise *)promise_delay_1s { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_1s"); resolver(@"PMKWhen promise_delay_1s"); }); }]; }
- (AnyPromise *)promise_delay_3s { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_3s"); resolver(@"PMKWhen promise_delay_3s"); }); }]; }
- (AnyPromise *)promise_delay_8s { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_8s"); resolver(@"PMKWhen promise_delay_8s"); }); }]; }
- (AnyPromise *)promise_delay_error { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolver) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"PMKWhen promise_delay_error"); resolver([NSError errorWithDomain:@"test" code:100 userInfo:nil]); }); }]; }
|
下面是执行结果:
2019-11-04 14:34:06.333085+0800 IDLFundationTest[2020:11831274] PMKWhen promise_delay_1s 2019-11-04 14:34:06.333917+0800 IDLFundationTest[2020:11831274] PMKWhen PMKWhen promise_delay_1s 2019-11-04 14:34:08.532678+0800 IDLFundationTest[2020:11831274] PMKWhen promise_delay_3s 2019-11-04 14:34:11.834737+0800 IDLFundationTest[2020:11831274] PMKWhen promise_delay_error 2019-11-04 14:34:14.034667+0800 IDLFundationTest[2020:11831274] PMKWhen promise_delay_8s
|
Present ViewController 后返回结果:
在刚接触iOS开发的时候感觉这部分没有Android来得便捷,在Android中直接通过onActivityResult回调就可以拿到上一个页面传递回来的值,而PromiseKit提供了这个便捷方法,下面是一个例子:
页面A,开启一个页面并等待页面的返回值
[self.viewHolder.presentViewControllerBtn bk_whenTapped:^{ [self promiseViewController:[IDLListKitViewController new] animated:YES completion:nil].then(^(id value){ NSLog(@"Return Value from last ViewController %@",value) }) }]
|
页面B,经过一系列处理后返回值给上一个界面,并关闭当前界面
[PMKPromise when:@{@"task1":[self promise_delay_1s],@"task2":[self promise_delay_8s],@"task3":@"linxiaohai"}].then(^(id value) { NSLog(@"%@",value); [self fulfill:value]; });
|
UIView 视图动画:
[UIView promiseWithDuration:3 animations:^{ self.viewHolder.pushViewControllerBtn.alpha = 0; }].then(^(){ });
|
获取Promise值:
Promise值是通过then来获取的,PromiseKit目前有三个方法用于Promise值的获取,分别是then,thenInBackground,以及thenOn,分别在主线程,后台线程,指定线程获取Promise值。
- (AnyPromise * __nonnull (^ __nonnull)(id __nonnull))then NS_REFINED_FOR_SWIFT; - (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))thenInBackground NS_REFINED_FOR_SWIFT; - (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, id __nonnull))thenOn NS_REFINED_FOR_SWIFT;
|
捕获异常:
在Promise reject 的时候,流程会走到catch分支,PromiseKit有两种catch方式来catch抛出的异常,一种在主线程,一种需要指定处理异常的线程。
- (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))catch NS_REFINED_FOR_SWIFT; - (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))catchInBackground NS_REFINED_FOR_SWIFT; - (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, id __nonnull))catchOn NS_REFINED_FOR_SWIFT;
|
善后处理:
在Promise 被 resolve的时候,不论结果是fullfill还是reject都会走到ensure这个分支。
- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull))ensure NS_REFINED_FOR_SWIFT; - (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, dispatch_block_t __nonnull))ensureOn NS_REFINED_FOR_SWIFT;
|
异步处理:
AnyPromise *dispatch_promise_on(dispatch_queue_t queue, id block);
|
在某个队列下执行block操作
AnyPromise *dispatch_promise(id block);
|
dispatch_promise(^{ return md5(input); }).then(^(NSString *md5){ NSLog(@"md5: %@", md5); });
|
在后台队列下执行block操作,如果要取消某个操作可以return NSError.一旦返回一个id对象就相当于启动了一个Promise链。