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