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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。