先來看一個開發中常見的嵌套類型的例子:數組
//通常的block回調嵌套
- (void) _normalExample
{
[self _normalBlock1:^(id value) {
[self _normalBlock2:^(id value) {
[self _normalBlock3:^(id value) {
[self _normalBlock4:^(id value) {
[self _normalBlock5:^(id value) {
[self _normalBlock6:^(id value) {
}];
}];
}];
}];
}];
}];
}
複製代碼
//正常blocks
- (void) _normalBlock1:(void(^)(id value))block
{
NSLog(@"_normalBlock1");
block(@(1));
}
- (void) _normalBlock2:(void(^)(id value))block
{
block(@"_normalBlock2");
}
- (void) _normalBlock3:(void(^)(id value))block
{
block(@"_normalBlock3");
}
- (void) _normalBlock4:(void(^)(id value))block
{
block(@"_normalBlock4");
}
- (void) _normalBlock5:(void(^)(id value))block
{
block(@"_normalBlock5");
}
- (void) _normalBlock6:(void(^)(id value))block
{
block(@"_normalBlock6");
}
複製代碼
有沒有被上面的嵌套整蒙?固然開發中嵌套如此多層的也有但很少見,然鵝這樣一坨代碼寫在那裏終歸不美觀(固然你要是以爲有層次感,那我也無法,不過話題仍是要繼續),很不利於後期的維護和修改。promise
那麼有沒有一種方式解決呢?下面引入今天的正題Promise
, 先看下用promise
實現的方式:bash
//採用promise
- (void) _promiseExample
{
[[[[[[[FBLPromise do:^id _Nullable{
return [self _promise1];
}] then:^id _Nullable(id _Nullable value) {
return [self _promise2];
}] then:^id _Nullable(id _Nullable value) {
return [self _promise3];
}] then:^id _Nullable(id _Nullable value) {
return [self _promise4];
}] then:^id _Nullable(id _Nullable value) {
return [self _promise5];
}] then:^id _Nullable(id _Nullable value) {
return [self _promise6];
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
複製代碼
//promises
- (FBLPromise *) _promise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise1");
}];
}
- (FBLPromise *) _promise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise2");
}];
}
- (FBLPromise *) _promise3
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise3");
}];
}
- (FBLPromise *) _promise4
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise4");
}];
}
- (FBLPromise *) _promise5
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise5");
}];
}
- (FBLPromise *) _promise6
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise6");
}];
}//promises
- (FBLPromise *) _promise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise1");
}];
}
- (FBLPromise *) _promise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise2");
}];
}
- (FBLPromise *) _promise3
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise3");
}];
}
- (FBLPromise *) _promise4
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise4");
}];
}
- (FBLPromise *) _promise5
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise5");
}];
}
- (FBLPromise *) _promise6
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_promise6");
}];
}
複製代碼
在調用層面是否是感受很舒服?並且調用的邏輯語義也顯而易見,若是你以爲很爽,那麼就接下來一塊兒聊聊promise
的使用方法和一些淺顯的邏輯原理,若是你以爲看着也不舒服,那就繼續用Block套block唄。網絡
如圖爲PromisesObjC
的框架結構,除去FBLPromise
、FBLPromiseError
以及頭文件,這些都是不一樣情形下的實現類,下面咱們先從經常使用的幾個入手,熟悉一下如何去使用promise
框架
如字面意思,這個特別適合一件事作完再作另外一件事的邏輯,如A,B兩個任務,B依賴於A任務的完成結果,設置一個場景,任務B依賴任務A返回的一個值,進行求和,而後輸出求和結果異步
- (void) _thenExample
{
[[[FBLPromise do:^id _Nullable{
return [self _thenPromise1];
}] then:^id _Nullable(id _Nullable value) {
return [self _thenPromise2:[value intValue]];
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"%@",value);
return nil;
}];
}
- (FBLPromise *) _thenPromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@(1));
}];
}
- (FBLPromise *) _thenPromise2:(int)input
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
int sum = input + 10;
fulfill(@(sum));
}];
}
複製代碼
do
暫且無論,咱們能夠進入then
方法查看源碼,它會返回一個FBLPromise
的對象,內部會調用[self chainOnQueue:queue chainedFulfill:work chainedReject:nil]
核心方法,至於這個核心方法中的邏輯咱們稍後講解原理的時候再說,能夠看到正是由於此方法返回的FBLPromise
,才能實現這種鏈式調用的形式,固然,你能夠寫成點調用的形式,以下:async
[FBLPromise do:^id _Nullable{
return [self _thenPromise1];
}].then(^ id (id value){
return [self _thenPromise2:[value intValue]];
}).then(^ id (id value){
NSLog(@"%@",value);
return nil;
});
複製代碼
但這種寫法then
中的參數不會自動補全,寫起來比較彆扭,因此,下面的例子我都以[]的形式書寫,畢竟是OC
的特有結構ui
總結:
then
可用做一個任務的執行依賴另外一個任務完成的結果的場景,開發中常見的是對網絡圖片加載完成後對圖片的特殊處理,如模糊,剪切等,或者網絡請求數據完成後對數據的處理等狀況,這裏再也不贅述,有興趣的可拿本身項目來練練。spa
Wait until all of the given promises are fulfilled.If one of the given promises is rejected, then the returned promise is rejected with same error.
源碼文檔中的解釋如上:當全部的promise
執行完成才執行另外一個任務,只要有一個promise
執行錯誤,則這個任務隊列直接結束,並返回此錯誤。此外,執行完成中若是返回值是NSError
的,也按照執行錯誤處理。簡而言之,只要全部的promise
都執行了fulfill()
且fulfill()
中的參數不能爲NSError
類型,纔算全部的任務執行成功。線程
這種情形在開發中很是常見,特別是在多個任務的情形,並且這些待執行的任務順序不分前後,可是在then
中的結果返回數組中的元素,通過驗證是和all
中數組的順序是一致的
假設一種場景,C任務在A,B任務都執行成功的時候才執行
/******************** all的使用 *********************/
- (void) _allExample
{
NSArray *promises = @[[self _allPromise1],[self _allPromise2]];
[[[[FBLPromise all:promises] then:^id _Nullable(NSArray * _Nullable value) {
NSLog(@"%@",value);
return [self _allPromise3];
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"%@",value);
return nil;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _allPromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
fulfill(@"_allPromise1 執行完成");
});
}];
}
- (FBLPromise *) _allPromise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_allPromise2 執行完成");
}];
}
- (FBLPromise *) _allPromise3
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_allPromise3 執行完成");
}];
}
複製代碼
輸出結果以下:
能夠看到
[FBLPromise all:promises] then:^id _Nullable(NSArray * _Nullable value) {
NSLog(@"%@",value);
return [self _allPromise3];
}]
複製代碼
promises
任務數組的順序和返回值value
的順序保持一致性
若是上述兩個任務有一個失敗,則會進入catch
中,這個留到後面講解
總結:all只有當給定的全部待執行的任務所有都成功執行,且返回值不能爲
NSError
類型,才能繼續接下來的任務執行,不然以第一個執行出錯的任務的錯誤做爲總體的錯誤返回,進入到catch
中。
此方法是用來捕捉錯誤的
/******************** catch的使用 *********************/
- (void) _catchExample
{
[[FBLPromise do:^id _Nullable{
return [self _catchPromise];
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _catchPromise
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
NSError *error = [NSError errorWithDomain:@"catch.example" code:0 userInfo:@{}];
reject(error);
// fulfill(error);
}];
}
複製代碼
上述代碼便可說明,這裏再也不詳細解釋
A block that always executes, no matter if the receiver is rejected or fulfilled.
複製代碼
源碼中的註釋是,不管接收者是執行成功仍是失敗,都會執行此block塊
- (void) _alwaysExample
{
[[[[FBLPromise do:^id _Nullable{
return [self _alwaysPromise1];
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"%@",value);
return [self _alwaysPromise2];
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}] always:^{
NSLog(@"老是會執行");
}];
}
- (FBLPromise *) _alwaysPromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
NSError *error = [NSError errorWithDomain:@"catch.example" code:0 userInfo:@{}];
fulfill(error);
}];
}
- (FBLPromise *) _alwaysPromise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_alwaysPromise2");
}];
}
複製代碼
輸出結果以下:
總結: always是必定會執行的block塊,不管前面的執行結果是成功仍是失敗
能夠理解爲
all
的對立面,只有當全部的任務執行都錯誤後才執行catch
,且捕捉到的是最後一次返回的錯誤,不然只要有一個任務執行成功,則執行成功。
/******************** any的使用 *********************/
- (void) _anyExample
{
[[[[FBLPromise any:@[[self _anyPromise1],[self _anyPromise2]]] then:^id _Nullable(NSArray * _Nullable value) {
return [self _anyPromise3];
}] then:^id _Nullable(id _Nullable value) {
NSLog(@"%@",value);
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _anyPromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_anyPromise1");
// NSError *error = [NSError errorWithDomain:@"any.example" code:1 userInfo:@{}];
// reject(error);
}];
}
- (FBLPromise *) _anyPromise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
NSError *error = [NSError errorWithDomain:@"any.example" code:0 userInfo:@{}];
reject(error);
}];
}
- (FBLPromise *) _anyPromise3
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_anyPromise3");
}];
}
複製代碼
通過驗證會發現,只有當_anyPromise1
_anyPromise2
全都執行reject
的時候,纔會執行catch
,而不會執行第一個then
中的_anyPromise3
,不然必定會執行第一個then
塊裏的內容,此外要注意的是,any
中給定的promises
數組必須所有執行完畢,纔會繼續決定執行then
或者reject
.
此方法建立一個
pending
狀態的promise
,且帶異步執行的block
塊,適合用來處理異步任務回來後的狀況,好比網絡請求或者耗時操做回來後要作某些事,就可使用此方法,此方法block
塊中的FBLPromiseAsyncWorkBlock
含有FBLPromiseFulfillBlock
和FBLPromiseRejectBlock
,能夠用來處理異步任務回來後決定是否須要繼續執行下一步任務,上面的例子中都用到了此種方式,能夠自行測驗,再也不附代碼說明
此方法是用來生成阻塞當前線程的
promise
,直到當此promise
執行完畢,相似於系統提供的方法sleep()
,它內部其實使用了信號量實現,此方法在特殊狀況下可用,通常使用比較少。
它內部使用了
dispatch_after
實現延遲執行的效果,此法簡單,再也不贅述
此方法和
any
效果相似,給定的promises
中只要有一個執行成功,則會執行then
以後的內容,只有所有執行失敗,纔會執行catch
,它和any
的區別是,race
給定的promises
數組只要有一個promise
執行完成,就會執行then
或者catch
,若是所有的promise
都執行,效果和any
同樣
/******************** race的使用 *********************/
- (void) _raceExample
{
[[[FBLPromise race:@[[self _racePromise1],[self _racePromise2]]] then:^id _Nullable(id _Nullable value) {
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _racePromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
// fulfill(@"_racePromise1");
NSError *error = [NSError errorWithDomain:@"race.example" code:2 userInfo:@{}];
reject(error);
}];
}
- (FBLPromise *) _racePromise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
// NSError *error = [NSError errorWithDomain:@"race.example" code:0 userInfo:@{}];
// reject(error);
}];
}
複製代碼
能夠對不一樣的promise
註銷fulfill
reject
來驗證
用一個新的
promise
去替換掉執行失敗的promise
,而不讓它執行到catch
中,可謂是偷龍換鳳,在某些場景下回用到,好比網絡請求回來後,若是失敗,咱們不想讓它走到catch
中報錯,就能夠用recover
去執行另外一個promise
/******************** recover的使用 *********************/
- (void) _recoverExample
{
[[[[self _recoverPromise1] recover:^id _Nullable(NSError * _Nonnull error) {
if (error) {
return [self _recoverPromise2];
}
return nil;
}] then:^id _Nullable(id _Nullable value) {
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _recoverPromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
// NSError *error = [NSError errorWithDomain:@"recover.example" code:0 userInfo:@{}];
// reject(error);
fulfill(@"_recoverPromise1");
}];
}
- (FBLPromise *) _recoverPromise2
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@"_recoverPromise2");
}];
}
複製代碼
上面的代碼能夠自行測驗,會更加明白此方法的效果
它是
promise
的一個實例方法,調用它的是一個promise
對象,reduce
數組是一個值數組,reduce
的做用就是用promise
和後面的值數組進行組合,每兩個組合可獲得一個promise
對象,而後用新的promise
對象和剩下的值進行組合,最終獲得一個promise
對象,在reduce
的block
塊中咱們能夠拿到一個promise
的返回值和另外一個值,咱們能夠對這兩個值作本身的邏輯處理,以下例子作的是乘積
/******************** reduce的使用 *********************/
- (void) _reduceExample
{
[[[[self _reducePromise1] reduce:@[@(10), @(20)] combine:^id _Nullable(id _Nullable partial, id _Nonnull next) {
return @([partial intValue] * [next intValue]);
}] then:^id _Nullable(id _Nullable value) {
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _reducePromise1
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@(3));
}];
}
複製代碼
設置超時時間,若執行時間大於超時時間,則會報錯,進入
catch
塊中,不然會進入then
中
/******************** timeout的使用 *********************/
- (void) _timeoutExample
{
[[[[self _timeoutPromise] timeout:2] then:^id _Nullable(id _Nullable value) {
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _timeoutPromise
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
fulfill(@"_timeoutPromise");
});
}];
}
複製代碼
驗證方法,此方法是實例方法,若是知足驗證邏輯,則會進入
then
,value是promise
成功後的回傳參數,不然會進入catch
中,error表明的是validate
錯誤
/******************** validate的使用 *********************/
- (void) _validateExample
{
[[[[self _validatePromise] validate:^BOOL(id _Nullable value) {
return ([value intValue] > 50);
}] then:^id _Nullable(id _Nullable value) {
return value;
}] catch:^(NSError * _Nonnull error) {
NSLog(@"%@",error);
}];
}
- (FBLPromise *) _validatePromise
{
return [FBLPromise async:^(FBLPromiseFulfillBlock _Nonnull fulfill, FBLPromiseRejectBlock _Nonnull reject) {
fulfill(@(20));
}];
}
複製代碼
ps:上面列舉了
promise
一些常見的方法,其中then
do
all
catch
的結合使用最爲常見,async
更常見於建立promise
對象,掌握這幾個基本就夠項目中使用了,下面說一說promise
中核心的兩個方法
- (void)observeOnQueue:(dispatch_queue_t)queue
fulfill:(FBLPromiseOnFulfillBlock)onFulfill
reject:(FBLPromiseOnRejectBlock)onReject {
NSParameterAssert(queue);
NSParameterAssert(onFulfill);
NSParameterAssert(onReject);
@synchronized(self) {
switch (_state) {
case FBLPromiseStatePending: {
if (!_observers) {
_observers = [[NSMutableArray alloc] init];
}
[_observers addObject:^(FBLPromiseState state, id __nullable resolution) {
dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
switch (state) {
case FBLPromiseStatePending:
break;
case FBLPromiseStateFulfilled:
onFulfill(resolution);
break;
case FBLPromiseStateRejected:
onReject(resolution);
break;
}
});
}];
break;
}
case FBLPromiseStateFulfilled: {
dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
onFulfill(self->_value);
});
break;
}
case FBLPromiseStateRejected: {
dispatch_group_async(FBLPromise.dispatchGroup, queue, ^{
onReject(self->_error);
});
break;
}
}
}
}
複製代碼
此方法首先判斷當前
promise
的state
,若是是FBLPromiseStateFulfilled
,直接執行onFulfill
回調,將value
值回調出去。若是是FBLPromiseStateRejected
,直接執行onReject
回調,將error
值回調出去。不然會檢查_observers
是否爲空,爲空則建立一個觀察者數組,此數組中存放的對象是FBLPromiseObserver
,它其實是一個block
塊typedef void (^FBLPromiseObserver)(FBLPromiseState state, id __nullable resolution);
是一個含有state
和resolution
參數的無返回值block
,此數組中的block
對象會延遲到最終的fulfill:
和reject:
方法中執行回調,在回到中根據state
值,肯定執行下一步操做,將對應的值返回出去。
- (FBLPromise *)chainOnQueue:(dispatch_queue_t)queue
chainedFulfill:(FBLPromiseChainedFulfillBlock)chainedFulfill
chainedReject:(FBLPromiseChainedRejectBlock)chainedReject {
NSParameterAssert(queue);
FBLPromise *promise = [[FBLPromise alloc] initPending];
__auto_type resolver = ^(id __nullable value) {
if ([value isKindOfClass:[FBLPromise class]]) {
[(FBLPromise *)value observeOnQueue:queue
fulfill:^(id __nullable value) {
[promise fulfill:value];
}
reject:^(NSError *error) {
[promise reject:error];
}];
} else {
[promise fulfill:value];
}
};
[self observeOnQueue:queue
fulfill:^(id __nullable value) {
value = chainedFulfill ? chainedFulfill(value) : value;
resolver(value);
}
reject:^(NSError *error) {
id value = chainedReject ? chainedReject(error) : error;
resolver(value);
}];
return promise;
}
複製代碼
這個方法的核心要點,也在observeOnQueue:
上,方法中首先定義了一個pending
狀態的promise
對象和resolver
回調,而後自身執行的是observeOnQueue:
方法,在回調裏,去執行resolver
,那麼resolver
中作了什麼呢?很簡單,根據回傳過來的值判斷是不是promise
類型,若是是,則對此promise
添加觀察者,不然直接執行fulfill
方法,我的認爲這裏能夠細化判斷是不是NSError
類型,從而更精確的執行reject:
或者fulfill:
,不過fulfill:
接收參數自己就是id
類型,且內部也作了NSError
的過濾,直接執行也是能夠的
只要理解了這兩個方法,那麼promise
的核心要點也就理解了,最後作一個簡單總結:
總結:簡而言之,
promise
的實現核心歸於對block
塊的延遲處理,結合GCD
以及鏈式調用的思想,從而能夠用優雅簡潔的方式來處理異步任務,若是想更深入地理解,能夠打斷點一步步調試