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