OCPromise-用OC实现JS的Promise
上篇文章介绍了OCPromise的基本用法,这篇里我将介绍几个扩展方法。

deliverOnMainThread

上篇结尾处提到过,OCPromise的任务都是在子线程上执行的,那么我们在接收任务结果时,如果需要进行UI的操作时,还需要自己手动切到主线程上,因此我提供了一个将任务结果传递到主线程的回调方法:

    OCPromise *p = Promise(^(resolve  _Nonnull resolve, reject  _Nonnull reject) {
        NSLog(@"start new Promise...");
        resolve(@123);
    });
    
    OCPromise *multiply = function(^OCPromise * _Nullable(id  _Nonnull value) {
        return Promise(^(resolve  _Nonnull resolve, reject  _Nonnull reject) {
            NSLog(@"calculating %ld x %ld ...", [value longValue], [value longValue]);
            resolve([NSNumber numberWithLong:[value longValue] * [value longValue]]);
        });
    });
    
    OCPromise *add = function(^OCPromise * _Nullable(id  _Nonnull value) {
        return Promise(^(resolve  _Nonnull resolve, reject  _Nonnull reject) {
            NSLog(@"calculating %ld + %ld ...", [value longValue], [value longValue]);
            resolve([NSNumber numberWithLong:[value longValue] + [value longValue]]);
        });
    });
    
    p.then(multiply).deliverOnMainThread(^(id  _Nonnull value) {
        NSLog(@"1 got value %@ on %@", value, [NSThread currentThread]);
    }).then(function(^OCPromise * _Nullable(id  _Nonnull value) {
        NSLog(@"2 got value %@ on %@", value, [NSThread currentThread]);
        return Promise(^(resolve  _Nonnull resolve, reject  _Nonnull reject) {
            resolve(@([value longValue] * 10));
        });
    })).then(add).deliverOnMainThread(^(id  _Nonnull value) {
        NSLog(@"3 got value %@ on %@", value, [NSThread currentThread]);
    }).then(function(^OCPromise * _Nullable(id  _Nonnull value) {
        NSLog(@"4 got value %@ on %@", value, [NSThread currentThread]);
        return nil;
    }));
2020-06-11 16:10:37.224503+0800 OCPromise_Example[56255:2885996] start new Promise...
2020-06-11 16:10:37.224690+0800 OCPromise_Example[56255:2885996] calculating 123 x 123 ...
2020-06-11 16:10:37.224909+0800 OCPromise_Example[56255:2885925] 1 got value 15129 on <NSThread: 0x6000022c85c0>{number = 1, name = main}
2020-06-11 16:10:37.224920+0800 OCPromise_Example[56255:2885996] 2 got value 15129 on <NSThread: 0x60000228a300>{number = 6, name = (null)}
2020-06-11 16:10:37.225055+0800 OCPromise_Example[56255:2885996] calculating 151290 + 151290 ...
2020-06-11 16:10:37.225182+0800 OCPromise_Example[56255:2885996] 4 got value 302580 on <NSThread: 0x60000228a300>{number = 6, name = (null)}
2020-06-11 16:10:37.225188+0800 OCPromise_Example[56255:2885925] 3 got value 302580 on <NSThread: 0x6000022c85c0>{number = 1, name = main}

通过打印结果我们可以看到1、3都是在主线程执行的,并且主线程的接收并不影响子线程任务结果在任务链中的传递。deliverOnMainThread的实际调用逻辑就是在子线程接收到上一个任务的结果,然后为主线程添加一个异步任务来执行外部回调将结果传出去,并在当前线程继续将任务结果传递给下一个任务。

map

map的作用就是改变上一个任务的结果并传递给下一个任务。这里提供了两种map方法,一种是实例方法:

    p.then(multiply).map(^id _Nullable(id  _Nonnull value) {
        NSLog(@"got value before map %@", value);
        return @([value longValue] * 10);
    }).then(function(^OCPromise * _Nullable(id  _Nonnull value) {
        NSLog(@"got value after map %@", value);
        return nil;
    }));
2020-06-11 16:20:59.395731+0800 OCPromise_Example[56282:2892198] start new Promise...
2020-06-11 16:20:59.396130+0800 OCPromise_Example[56282:2892198] calculating 123 x 123 ...
2020-06-11 16:20:59.396406+0800 OCPromise_Example[56282:2892198] got value before map 15129
2020-06-11 16:20:59.396642+0800 OCPromise_Example[56282:2892198] got value after map 151290

这种map方式很好理解,接收到上一个任务的结果之后,执行回调,在回调内针对结果进行处理之后在return回去。这样下一个任务接收到的入参就是已经处理过的值了。

另外一种是静态方法:

    OCPromise *map = OCPromise.map(@[p, add, multiply], ^id _Nullable(id  _Nonnull value) {
        return @([value longValue] * 10);
    });
    
    p.then(map).then(function(^OCPromise * _Nullable(id  _Nonnull value) {
        NSLog(@"got value %@", value);
        return nil;
    }));
2020-06-11 16:25:46.242822+0800 OCPromise_Example[56319:2895774] start new Promise...
2020-06-11 16:25:46.243068+0800 OCPromise_Example[56319:2895772] start new Promise...
2020-06-11 16:25:46.243111+0800 OCPromise_Example[56319:2895773] calculating 123 + 123 ...
2020-06-11 16:25:46.243134+0800 OCPromise_Example[56319:2895770] calculating 123 x 123 ...
2020-06-11 16:25:46.243378+0800 OCPromise_Example[56319:2895774] got value (
1230,
2460,
151290

)

有没有发现和OCPromise.all很像?没错,OCPromise.map就是OCPromise.all和实例方法map的结合,OCPromise.map会用传进来的任务数组生成一个all promise的对象,而数组内的每个任务的执行结果都会再调用传入的mapBlock获得一个新的值并最终存到结果数组中。

retry

retry顾名思义,就是在任务执行失败(reject)后进行重试。我们先看下函数定义:

OCPromise * retry(OCPromise *ocPromise, uint8_t times, int64_t delay/*ms*/);

入参一共有三个,第一个是需要执行的任务,第二个是重试的次数,第三个是每次重试的间隔时间(注意是毫秒值),返回值是一个OCPromise对象。
函数的定义很好理解,我们只需要用retry函数创建一个OCPromise对象并对这个对象的执行结果进行监听就好了。
应用场景常见的就是网络请求的重试,下面我用一个模拟的网络请求来举例说明一下retry的用法:

- (void)startRequest {
    NSDictionary *params = @{@"page":@(self.page)};
    retry(OCPromise.resolve(params).then(self.requestPageData), 3, 200).then(function(^OCPromise * _Nullable(id  _Nonnull value) {
        NSLog(@"%@", value);
        return nil;
    })).catch(^(id  _Nonnull value) {
        NSLog(@"%@", value);
    });
}

- (OCPromise *)requestPageData {
    return function(^OCPromise * _Nullable(id  _Nonnull value) {
        return Promise(^(resolve  _Nonnull resolve, reject  _Nonnull reject) {
            [self requestDataWithParams:value completion:^(id data, NSError *error) {
                if (error) {
                    reject(error);
                } else {
                    resolve(data);
                }
            }];
        });
    });
}

- (void)requestDataWithParams:(NSDictionary *)params completion:(void (^) (id data, NSError *error))completion {
    NSLog(@"start request");
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //completion([NSString stringWithFormat:@"response data with request params %@", params], nil);
        completion(nil, [NSError errorWithDomain:(@"com.ocpromise.response.err") code:30001 userInfo:@{@"description":@"error"}]);
    });
}
2020-06-11 16:46:55.107129+0800 OCPromise_Example[56431:2909131] start request
2020-06-11 16:46:56.323393+0800 OCPromise_Example[56431:2909131] start request
2020-06-11 16:46:57.624308+0800 OCPromise_Example[56431:2909130] start request
2020-06-11 16:46:58.674446+0800 OCPromise_Example[56431:2909032] Error Domain=com.ocpromise.response.err Code=30001 "(null)" UserInfo={description=error}

我们用requestDataWithParams函数模拟了一个网络请求,内部用一个延时函数模拟请求的延时,并且返回一个请求失败的结果。然后我们将这个网络请求包装成一个OCPromise对象requestPageData,当执行startRequest方法时,我们生成了针对requestPageData的retry函数,并对结果进行监听,当我们的请求失败了三次之后catch到了最终结果。

以上就是基于OCPromise的基本方法有扩展的一些使用方法。

大家如果感兴趣可以下载这个库,帮忙给找找bug,提提意见,帮助我学习些新的思路或者扩展一下应用场景。感谢!

github:OCPromise


DOS魔王
1 声望0 粉丝