OCPromise是参考的Javescript中的Promise写的一套用Objective-C实现的异步任务队列管理的链式操做工具。git
写这套库的想法是源自一次面试的失败经历:以前在工做中我使用过React native进行开发,所以也写过Javascript代码而且使用过Promise语法,可是在一次面试中,面试官让我手写Promise的实现,当时我直接懵了,才发如今开发过程当中不少东西实现过一次以后,后面再用到时直接复制粘贴再改一改,结果就是这些东西根本没有变成本身的知识,甚至对它的理解也很片面。github
回想起来,Promise的调用方式仍是颇有意思的,链式的语法使用起来也很美观,所以我尝试用OC实现了Promise的功能,OCPromise提供的功能能够参考这篇关于JS-Promise的文章:理解 Javascript 中的 Promise,我在写OCPromise时也是彻底参照的Promise,只不过因为语法的差别性,调用方法会略有不一样。面试
下面我先介绍一下OCPromise的使用方法:segmentfault
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]]); }); });
使用Promise()函数建立的Promise对象能够处理独立的任务,而使用function()函数建立Promise对象时,实际Promise对象的建立延迟到^OCPromise *(id value) {}执行时期,而且Promise的任务执行期间value能够参与内部的Promise()任务的执行的(也能够不参与)。数组
p. then(multiply). then(add). then(multiply). then(add). then(function(^OCPromise * _Nullable(id _Nonnull value) { NSLog(@"Got value: %@",value); return nil; }));
打印结果安全
2020-05-29 15:33:34.955691+0800 OCPromise_Example[80577:17114562] start new Promise...
2020-05-29 15:33:34.956269+0800 OCPromise_Example[80577:17114562] calculating 123 x 123 ...
2020-05-29 15:33:34.957493+0800 OCPromise_Example[80577:17114562] calculating 15129 + 15129 ...
2020-05-29 15:33:34.958875+0800 OCPromise_Example[80577:17114562] calculating 30258 x 30258 ...
2020-05-29 15:33:34.960475+0800 OCPromise_Example[80577:17114562] calculating 915546564 + 915546564 ...
2020-05-29 15:33:34.961727+0800 OCPromise_Example[80577:17114562] Got value: 1831093128
能够看到,当Promise执行了resolve(),任务确实被串了起来顺序的执行。而且这里咱们须要注意,使用function()函数构建Promise函数时,^OCPromise *(id value) {}并不能经过外部进行触发执行,而是由上一个Promise对象执行完resolve()进行触发的。并发
刚才示例中Promise都是执行的resolve(),这表示任务处理成功,而对应的reject()则是触发异常状况,针对任务队列的异常捕获咱们要用到catch()函数。
finally()函数是在任务队列执行完毕后触发执行的,不管整个任务队列成功完成仍是出现了异常都会执行,咱们能够在这里进行一些最终处理,好比加载动画的关闭或者最终数据的处理等。
下面咱们来看一下reject与catch的配合使用以及finally的使用方法异步
//增长一个触发reject的Promise对象 OCPromise *doReject = function(^OCPromise * _Nullable(id _Nonnull value) { return Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { NSLog(@"receive %ld",[value longValue]); if ([value longValue] > 1000) { reject(@"opps, number is too big"); } else { resolve(value); } }); }); p. then(multiply). then(doReject). then(add). catch(^(id _Nonnull value) { NSLog(@"catch error, reason is \"%@\"",value); }). finally(^(id _Nonnull value) { NSLog(@"final value is \"%@\"",value); });
打印结果函数
2020-05-29 16:17:49.402107+0800 OCPromise_Example[80859:17146759] start new Promise...
2020-05-29 16:17:49.402549+0800 OCPromise_Example[80859:17146759] calculating 123 x 123 ...
2020-05-29 16:17:49.403076+0800 OCPromise_Example[80859:17146759] receive 15129
2020-05-29 16:17:49.403401+0800 OCPromise_Example[80859:17146759] catch error, reason is "opps, number is too big"
2020-05-29 16:17:49.403814+0800 OCPromise_Example[80859:17146759] final value is "opps, number is too big"
在执行到doReject时入参value大于1000,执行了reject(),所以后面的add并无执行,直接执行了catch和finally。工具
OCPromise.resolve(@"Just do it!").then(function(^OCPromise * _Nullable(id _Nonnull value) { NSLog(@"%@",value); return nil; }));
2020-05-29 16:29:39.036376+0800 OCPromise_Example[80944:17156364] Just do it!
OCPromise.reject(@"Oops!").catch(^(id _Nonnull value) { NSLog(@"%@",value); });
2020-05-29 16:31:39.463002+0800 OCPromise_Example[80971:17158013] Oops!
OCPromise.resolve和OCPromise. reject其实就是两个简单的触发器,是建立单一指责任务模块的快捷方式,由这两个静态方法建立的Promise对象不受外部条件的影响,而且仅能触发正常执行/抛出异常一种模式。
应用场景例如OCPromise.resolve能够做为任务队列的触发函数:
OCPromise.resolve(@123).then(multiply).then(add);
或者:
p.then(function(^OCPromise * _Nullable(id _Nonnull value) { if ([value longValue]>1000) { return OCPromise.resolve(value); //here!!! } else { return OCPromise.reject(@"Oops,got error"); //here!!! } })).then(function(^OCPromise * _Nullable(id _Nonnull value) { NSLog(@"got value %@", value); return nil; })).catch(^(id _Nonnull value) { NSLog(@"catch error %@",value); });
OCPromise.all接收一组容纳Promise对象的数组,并将这些Promise对象打包生成一个新的Promise对象,这个Promise被触发执行时,内部的Promise数组中的任务开始异步并发执行,并在全部任务都完成时回调完成,这部分和GCD的dispatch_group_notify相似。
多个任务中只要有一个任务出现异常,则会执行reject抛出第一个发生的异常。
若是接收到一个空数组则直接执行resolve。
OCPromise *task1 = function(^OCPromise * _Nullable(id _Nonnull value) { return Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { NSLog(@"task1 needs sleep 4sec"); sleep(4); NSLog(@"task1 woke up"); resolve([NSString stringWithFormat:@"task1 checked %@",value]); }); }); OCPromise *task2 = Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { NSLog(@"task2 needs sleep 1sec"); sleep(1); NSLog(@"task2 woke up"); resolve(@"task2 is fine"); }); OCPromise *task3 = function(^OCPromise * _Nullable(id _Nonnull value) { return Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { NSLog(@"task3 needs sleep 3sec"); sleep(3); NSLog(@"task3 wokeup"); resolve([NSString stringWithFormat:@"task3 ignored %@",value]); }); }); OCPromise *all = OCPromise.all(@[task1, task2, task3]); OCPromise.resolve(@"the wallet").then(all).then(function(^OCPromise * _Nullable(id _Nonnull value) { NSLog(@"got value %@", value); return nil; }));
2020-06-01 17:51:42.608045+0800 OCPromise_Example[89417:18881922] task1 needs sleep 4sec
2020-06-01 17:51:42.608099+0800 OCPromise_Example[89417:18881919] task3 needs sleep 3sec
2020-06-01 17:51:42.608132+0800 OCPromise_Example[89417:18881925] task2 needs sleep 1sec
2020-06-01 17:51:43.609261+0800 OCPromise_Example[89417:18881925] task2 woke up
2020-06-01 17:51:45.609812+0800 OCPromise_Example[89417:18881919] task3 wokeup
2020-06-01 17:51:46.612289+0800 OCPromise_Example[89417:18881922] task1 woke up
2020-06-01 17:51:46.613935+0800 OCPromise_Example[89417:18881920] got value (
task1 checked the wallet, task2 is fine, task3 ignored the wallet
)
能够看到,Promise数组因为是异步并发的,因此任务执行的顺序是随机不固定的,三个任务耗时分别是4秒、1秒、3秒,最终执行完毕时耗时4秒,并返回一个结果数组,数组内值的顺序和Promise数组的任务顺序一致。
为保证结果数组内值的顺序,而且支持存储nil值,其实这里返回的数组是一个自定义的NSObject对象,并实现了数组的一些简单调用方法:经过下标进行取值value[0]、value[1],objectAtIndex,forin,enumerateObjectsUsingBlock。
Promise数组内也支持直接传值,内部会转成OCPromise.resolve。
OCPromise *all = OCPromise.all(@[@"Goodjob", @666, OCPromise.resolve(nil)]); all.then(function(^OCPromise * _Nullable(id _Nonnull value) { NSLog(@"first obj %@", value[0]); NSLog(@"second obj %@", [value objectAtIndex:1]); for (id obj in value) { NSLog(@"forin obj %@",obj); } [value enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSLog(@"enumerate block at %ld obj %@",idx, obj); }]; return nil; }));
2020-06-01 18:08:47.032494+0800 OCPromise_Example[89678:18896195] first obj Goodjob
2020-06-01 18:08:47.033058+0800 OCPromise_Example[89678:18896195] second obj 666
2020-06-01 18:08:47.033555+0800 OCPromise_Example[89678:18896195] forin obj Goodjob
2020-06-01 18:08:47.034477+0800 OCPromise_Example[89678:18896195] forin obj 666
2020-06-01 18:08:47.035326+0800 OCPromise_Example[89678:18896195] forin obj (null)
2020-06-01 18:08:47.036120+0800 OCPromise_Example[89678:18896195] enumerate block at 0 obj Goodjob
2020-06-01 18:08:47.036878+0800 OCPromise_Example[89678:18896195] enumerate block at 1 obj 666
2020-06-01 18:08:47.037486+0800 OCPromise_Example[89678:18896195] enumerate block at 2 obj (null)
OCPromise.race也是多任务并发处理的集合,Promise的建立过程和OCPromise.all相同,只不过完成的条件再也不是全部任务所有完成,而是竞争模式,当任意一个任务率先完成,不管成功仍是失败,都会直接将该结果回调,其他任务结果则丢弃再也不处理。
OCPromise.race(@[@666, OCPromise.reject(@"oops")]).then(function(^OCPromise * _Nullable(id _Nonnull value) { NSLog(@"got value %@", value); return nil; })).catch(^(id _Nonnull value) { NSLog(@"got error %@", value); });
两次不一样的返回结果:
2020-05-29 18:14:43.770779+0800 OCPromise_Example[81758:17233041] got value 6662020-05-29 18:13:13.503533+0800 OCPromise_Example[81745:17231231] got error oops
应用的场景例如经过不一样的接口请求相同的资源,或者为某个耗时操做添加超时操做等。
OCPromise *dealTask = Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { sleep(5); //模拟耗时操做 resolve(@"done"); }); OCPromise *timeout = Promise(^(resolve _Nonnull resolve, reject _Nonnull reject) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ reject(@"time out"); }); }); NSLog(@"task start"); OCPromise.race(@[dealTask, timeout]).then(function(^OCPromise * _Nullable(id _Nonnull value) { NSLog(@"result is %@", value); return nil; })).catch(^(id _Nonnull value) { NSLog(@"%@", value); });
2020-05-29 18:22:01.598934+0800 OCPromise_Example[81826:17238770] task start
2020-05-29 18:22:04.601462+0800 OCPromise_Example[81826:17238841] time out
以上就是OCPromise提供的基本功能了,有几点须要注意的:
以上就是OCPromise库的基本用法,可能没有Javascript的Promise那么灵活,但愿看到的您能多给提提意见!下一篇文章我介绍一下针对OCPromise的基本实现作的一些扩展。谢谢!
github:OCPromise