PromiseKit 概要

对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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容

  • 本文适用的读者 本文写给有一定Promise使用经验的人,如果你还没有使用过Promise,这篇文章可能不适合你,...
    HZ充电大喵阅读 7,311评论 6 19
  • 你不知道JS:异步 第三章:Promises 在第二章,我们指出了采用回调来表达异步和管理并发时的两种主要不足:缺...
    purple_force阅读 2,070评论 0 4
  • Promiese 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,语法上说,Pr...
    雨飞飞雨阅读 3,360评论 0 19
  • 00、前言Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区...
    夜幕小草阅读 2,134评论 0 12
  • 走马 公子宛. 两个人站在阴暗的角落注视着街上的一举一动,天黑了很久了有亮的迹象却还未亮,即使...
    公子宛阅读 286评论 0 0