iOS开发经验实用工具

PromiseKit 概要

2018-01-08  本文已影响1040人  不拘小节123456

对PromiseKit理解

PromiseKit 只是 Promise 设计模式的一种实现方式。并不能为我们的 app 带来任何”看得到“的好处,而仅仅是为了满足我们对“代码美学”的追求。因此,使用不使用 PromiseKit 完全取决于你自己的喜好。有的程序员不喜欢 Promise,因为他们觉得原有的异步编程中的回调块或委托模型完全就够用了,没有必要再用 Promise 进行重构。但毋庸置疑的一点是,Promise 确实能够让我们的异步编程代码显得更加优雅,可读性和维护性也更高。

所谓 Promise 设计模式,是借用生活中的“承诺”一词来描述异步编程中的回调模型。承诺是一种对未来的期许,它有两个特点,一,它描述的是未来的一种状态或动作,而不是目前的;二,承诺有一定的不确定性,承诺可以兑现(fullfill),也可能被拒绝(rejectd),拒绝的原因是各种各样的,有可能是承诺者不想兑现了,有可能是因为条件无法满足。举例,如果我们编写一个方法从网络获取图片,网络操作一般都是异步的,所以完全适用于承诺模式。所以这个方法就是一个承诺。它不用直接返回一个 Image,而是返回一个承诺对象,只有当网络请求到需要的数据时,这个承诺才会兑现(返回一个 Image 对象给调用者),否则承诺被拒绝(网络错误或者图片不存在),无论兑现还是拒绝,这个承诺对象都会标记为已解决(resolve),已解决的承诺会被销毁。如果既不兑现,也不拒绝,则这个承诺会一直有效(即未解决)。

PromiseKit使用

基本用法

-(void)jsonPostUrl:(NSString*)url params:(NSDictionary<NSString *,id>*)params success:(nullable void (^)(NSURLSessionDataTask *task, NSDictionary* responseDic))success
           failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure{

    [[self postUrl:url params:params requestType:@"json" success:^(NSURLSessionDataTask *task, id responseObject) {
        if([responseObject isKindOfClass:[NSDictionary class]]){
            NSDictionary* dic = (NSDictionary*)responseObject;
            NSNumber* code = dic[@"code"];
            if(![code isEqual:@0]){
//                [self.view makeToast:dic[@"msg"] duration:2 position:CSToastPositionCenter];
                if(failure){
                    NSError *err = [NSError errorWithDomain:@"ServiceError" code:code.integerValue userInfo:@{NSLocalizedDescriptionKey: dic[@"msg"]}];
                    failure(task,err);
                }
            }else if(success!=nil){
                success(task,dic);
            }
        }
    } failure:failure]resume];

}

修改后代码

-(AnyPromise*)jsonPostUrl:(NSString*)url params:(NSDictionary<NSString *,id>*)params{
    return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
        [[self postUrl:url params:params requestType:@"json" success:^(NSURLSessionDataTask *task, id responseObject) {
            if([responseObject isKindOfClass:[NSDictionary class]]){
                NSDictionary* dic = (NSDictionary*)responseObject;
                NSNumber* code = dic[@"code"];
                if(![code isEqual:@0]){
                    //                [self.view makeToast:dic[@"msg"] duration:2 position:CSToastPositionCenter];
                    NSError *err = [NSError errorWithDomain:@"ServiceError" code:code.integerValue userInfo:@{NSLocalizedDescriptionKey: dic[@"msg"]}];
                    resolve(err);
                }else{
                    resolve(dic);
                }
            }
        } failure:^(NSURLSessionDataTask *task, NSError *error) {
            resolve(error);
        }] resume];
    }];

}

[self loadAlbums:radioId pageNum:pageNum pageSize:pageSize].then(^(NSArray<AlbumModel>* array){
        if(pageNum <= 1){
            [_models removeAllObjects];
        }
        if(array!=nil){
            [_models addObjectsFromArray: array];
        }
    //下一个then中对应的promise
   AnyPromise *newPromise1 = [AnyPromise promiseWithAdapterBlock:^(PMKAdapter  _Nonnull adapter) {
        id errorValue;
        NSDictionary *valueDic1 = @{
                                   @"key":@"value1"
                                   };
        adapter(valueDic1,errorValue);
    }];
  //在thenOn中的data则是valueDic1
      return newPromise1;
      /**
         //如果没有return数据则在下面的thenOn中获取不到数据
        return _models;
        当ruturn error类型的数据则终止返回错误
        return [NSError errorWithDomain:PMKErrorDomain code:PMKUnexpectedError userInfo:@{NSLocalizedDescriptionKey: @"reason"}];
      */
    }).thenOn(dispatch_get_main_queue(),^(id data){
        //.....
    }).catch(^(NSError* error){
      //失败处理
    });

比较总结:
优点:
1.方法省去了 success 和 failure 两个异步块参数。
2.方法返回了一个 AnyPromise 对象。这是 PromiseKit 提供的主要的类,用于实现 Promise 模式。承诺不会执行,当然也不需要实现,但在将来特定时候会执行的(当然需要在未来实现,特别是调用这个方法时实现)动作。也就是说。success 和 failure 的功能由 AnyPromise 替代了。
3.方法中调用了 AnyPromise 的 promiseWithResolverBlock 方法来构造和返回 AnyPromise。这个方法会提供一个 resolve 参数。你可以把它仍然看成是一个回调块。只不过这个回调块同时充当了 success 和 failure。如果 resolve()传递了一个 NSError,则表明这个承诺被拒绝(即 failure),否则承诺会被兑现(success)。
缺点:
仍然需要做一些封装,通常是通过分类的方式扩展

其他用法:
when:可以等到多个异步全部都完成之后在做处理
join:和when类似等待所有异步回调都成功之后就返回成功的回调,不同的是reject部分,join是当所有promise都遍历一遍之后才会执行reject()回调,when是只要发现一个失败了立刻执行reject()回调
race:只要有一个promise返回成功则会调用then里面的回调,也就是第一个胜出.目前只有swift的代码,但是可以仿照when和join的逻辑进行扩展
always:不论promise成功失败与否都会调用该回调
catch:promise失败会调用
例子:

   AnyPromise *newPromise1 = [AnyPromise promiseWithAdapterBlock:^(PMKAdapter  _Nonnull adapter) {
        id errorValue;
        NSDictionary *valueDic = @{
                                   @"key":@"value1"
                                   };
        adapter(valueDic,errorValue);
    }];
    AnyPromise *newPromise2 = [AnyPromise promiseWithAdapterBlock:^(PMKAdapter  _Nonnull adapter) {
        id errorValue;
        NSDictionary *valueDic = @{
                                   @"key":@"value2"
                                   };
        adapter(valueDic,errorValue);
    }];
    NSArray *promiseArr = @[
                            newPromise1,
                            newPromise2,
                            ];
    AnyPromise *allPromise = PMKWhen(promiseArr);
    allPromise.then(^(id data){
        
        NSLog(@"data - 3 data = %@",data);
    }).always(^(){
        NSLog(@"always");
    });;

打印结果:从结果可以知道这钟情况的then可以得到最终所有异步情况的所有数据

Promise-OC[1139:29281] data - 3 data = (
        {
        key = value1;
    },
        {
        key = value2;
    }
)

原理篇

when原理,主要是通过for循环遍历所有promise,然后都成功之后在resolve(result)所有结果

AnyPromise *PMKWhen(id promises) {
    if (promises == nil)
        return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKWhen(nil)"}]];

    if ([promises isKindOfClass:[NSArray class]] || [promises isKindOfClass:[NSDictionary class]]) {
        if ([promises count] == 0)
            return [AnyPromise promiseWithValue:promises];
    } else if ([promises isKindOfClass:[AnyPromise class]]) {
        promises = @[promises];
    } else {
        return [AnyPromise promiseWithValue:promises];
    }

#ifndef PMKDisableProgress
    NSProgress *progress = [NSProgress progressWithTotalUnitCount:(int64_t)[promises count]];
    progress.pausable = NO;
    progress.cancellable = NO;
#else
    struct PMKProgress {
        int completedUnitCount;
        int totalUnitCount;
        double fractionCompleted;
    };
    __block struct PMKProgress progress;
#endif

    __block int32_t countdown = (int32_t)[promises count];
    BOOL const isdict = [promises isKindOfClass:[NSDictionary class]];

    return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
        NSInteger index = 0;

        for (__strong id key in promises) {
            AnyPromise *promise = isdict ? promises[key] : key;
            if (!isdict) key = @(index);

            if (![promise isKindOfClass:[AnyPromise class]])
                promise = [AnyPromise promiseWithValue:promise];

            [promise __pipe:^(id value){
                if (progress.fractionCompleted >= 1)
                    return;

                if (IsError(value)) {//这部分和join方法处理不同
                    progress.completedUnitCount = progress.totalUnitCount;

                    NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[value userInfo] ?: @{}];
                    userInfo[PMKFailingPromiseIndexKey] = key;
                    [userInfo setObject:value forKey:NSUnderlyingErrorKey];
                    id err = [[NSError alloc] initWithDomain:[value domain] code:[value code] userInfo:userInfo];
                    resolve(err);
                }
                else if (OSAtomicDecrement32(&countdown) == 0) {
                    progress.completedUnitCount = progress.totalUnitCount;

                    id results;
                    if (isdict) {
                        results = [NSMutableDictionary new];
                        for (id key in promises) {
                            id promise = promises[key];
                            results[key] = IsPromise(promise) ? ((AnyPromise *)promise).value : promise;
                        }
                    } else {
                        results = [NSMutableArray new];
                        for (AnyPromise *promise in promises) {
                            id value = IsPromise(promise) ? (promise.value ?: [NSNull null]) : promise;
                            [results addObject:value];
                        }
                    }
                    resolve(results);
                } else {
                    progress.completedUnitCount++;
                }
            }];
        }
    }];
}

join原理,也是通过for循环处理,只是循环内部对于reject部分与when不同

AnyPromise *PMKJoin(NSArray *promises) {
    if (promises == nil)
        return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKJoin(nil)"}]];

    if (promises.count == 0)
        return [AnyPromise promiseWithValue:promises];

    return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
        NSPointerArray *results = NSPointerArrayMake(promises.count);
        __block atomic_int countdown = promises.count;
        __block BOOL rejected = NO;

        [promises enumerateObjectsUsingBlock:^(AnyPromise *promise, NSUInteger ii, BOOL *stop) {
            [promise __pipe:^(id value) {

                if (IsError(value)) {//这部分和when方法处理不同
                    rejected = YES;
                }

                //FIXME surely this isn't thread safe on multiple cores?
                [results replacePointerAtIndex:ii withPointer:(__bridge void *)(value ?: [NSNull null])];

                atomic_fetch_sub_explicit(&countdown, 1, memory_order_relaxed);

                if (countdown == 0) {
                    if (!rejected) {
                        resolve(results.allObjects);
                    } else {
                        id userInfo = @{PMKJoinPromisesKey: promises};
                        id err = [NSError errorWithDomain:PMKErrorDomain code:PMKJoinError userInfo:userInfo];
                        resolve(err);
                    }
                }
            }];

            (void) stop;
        }];
    }];
}

使用范围总结

1,具有强依赖关系的异步任务
2,对某些系统的delegate回调方式修改成block回调(完全凭借个人喜好)
实例代码

@implementation CLLocationManager (PromiseKit)

+ (PMKPromise *)promise {
    return [PMKLocationManager promise];
}

@end

@interface PMKLocationManager : CLLocationManager <CLLocationManagerDelegate>
@end

@implementation PMKLocationManager {
    PMKResolver resolve;
    id retainCycle;
}

+ (PMKPromise *)promise {
    PMKLocationManager *manager = [PMKLocationManager new];
    manager.delegate = manager;
    manager->retainCycle = self;  // prevent deallocation
    [manager startUpdatingLocation];
    return [[AnyPromise alloc] initWithResolve:&manager->resolve];
}

- (void)locationManager:(id)manager didUpdateLocations:(NSArray *)locations {
    resolve(PMKManifold(locations.firstObject, locations));
    retainCycle = nil;  // break retain cycle
}

- (void)locationManager:(id)manager didFailWithError:(NSError *)error {
    resolve(error);
    retainCycle = nil;  // break retain cycle
}

@end

安装遇到的问题:
The “Swift Language Version” (SWIFT_VERSION) build setting must be set to a supported value for targ

这个时候需要设置swift版本 屏幕快照 2018-01-08 上午11.52.49.png

demo:https://github.com/riceForChina/PromiseKitDemo.git
参考文章:
http://blog.csdn.net/kmyhy/article/details/56277524
https://stackoverflow.com/questions/45079502/xcode-9-swift-dependency-analysis-error

上一篇 下一篇

猜你喜欢

热点阅读