在iOS中咱們通常使用delegate(代理)或者block(閉包)來進行異步操做,當要執行多個異步操做,必須將第二個嵌套在第一個的完成內,而且還要正常處理錯誤。這使得代碼結構異常的混亂,不方便查看。git
相信碼過JS
的同窗都清楚,在es6中新增promise
的語法,今後異步操做更加的靈活,它使得回調與事件進行了組合,並且寫法優美。谷歌在18年上半年開源了promises庫,使iOS上的異步編程更加便捷,其使用方式與JS中的promise
一致,並支持OC與Swift。es6
Promise
表明異步執行的結果或者錯誤信息。其擁有如下三種狀態github
當狀態發生變動後,就沒法再次變動其狀態。同時promise
不限制其訂閱者,當狀態發生變化後,全部的訂閱者都能接收到通知。同時訂閱者能夠返回另外一個promise
對象,從而造成管道通訊。編程
管道通訊過程能夠自由的使用線程swift
從上圖中能夠得知目前支持的操做,咱們能夠在FBLPromise
文件查找Promise
的實現原理api
//三種狀態類型
typedef NS_ENUM(NSInteger, FBLPromiseState) {
FBLPromiseStatePending = 0,
FBLPromiseStateFulfilled,
FBLPromiseStateRejected,
};
//閉包的回調類型
typedef void (^FBLPromiseObserver)(FBLPromiseState state, id __nullable resolution);```
複製代碼
@implementation FBLPromise {
//當前promise的狀態
FBLPromiseState _state;
//存放執行相關邏輯的結果存儲
id __nullable _value;
//存放執行相關邏輯的錯誤
NSError *__nullable _error;
//存放訂閱者的信息
NSMutableArray<FBLPromiseObserver> *_observers;
}
複製代碼
完成或失敗的操做實際上是遍歷全部訂閱者,將當前的狀態和執行後的結果通知訂閱者。執行完畢後改變緩存的狀態值,就算後期再次調用也不會響應。達到只要狀態變動就沒法再次啓動的效果。promise
- (void)fulfill:(nullable id)value { if ([value isKindOfClass:[NSError class]]) { [self reject:(NSError *)value]; } else { @synchronized(self) { if (_state == FBLPromiseStatePending) { //變動狀態 _state = FBLPromiseStateFulfilled; _value = value; _pendingObjects = nil; //通知訂閱者所訂閱的信息 for (FBLPromiseObserver observer in _observers) { observer(_state, _value); } _observers = nil; dispatch_group_leave(FBLPromise.dispatchGroup); } } } } 複製代碼
訂閱相關信息時,先去判斷當前的狀態類型,當此時已經執行完畢後,會立刻回調相關的執行結果。緩存
- (void)observeOnQueue:(dispatch_queue_t)queue fulfill:(FBLPromiseOnFulfillBlock)onFulfill reject:(FBLPromiseOnRejectBlock)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操做的重中之重,也是咱們promise中使用最多的原理所在,從FBLPromise+Then
中能夠看出該方法是then
的原理,也是實現管道通訊的原理bash
- (FBLPromise *)chainOnQueue:(dispatch_queue_t)queue chainedFulfill:(FBLPromiseChainedFulfillBlock)chainedFulfill chainedReject:(FBLPromiseChainedRejectBlock)chainedReject { //建立新的promise FBLPromise *promise = [[FBLPromise alloc] initPending]; //定義執行的閉包,該閉包是管道通訊的精華,也是爲何下一個then中能收到當前then中返回promise執行結果的關鍵 __auto_type resolver = ^(id __nullable value) { //判斷then返回的是不是promise if ([value isKindOfClass:[FBLPromise class]]) { //訂閱then返回的promise,主要是爲了調用下一個then [(FBLPromise *)value observeOnQueue:queue fulfill:^(id __nullable value) { [promise fulfill:value]; } reject:^(NSError *error) { [promise reject:error]; }]; } else { //若返回的不是promise中,則立刻傳給下一個then [promise fulfill:value]; } }; //訂閱當前的promise [self observeOnQueue:queue fulfill:^(id __nullable value) { //執行then操做後的返回值,多是promise也多是其餘值 value = chainedFulfill ? chainedFulfill(value) : value; //調用上面建立的閉包 resolver(value); } reject:^(NSError *error) { id value = chainedReject ? chainedReject(error) : error; resolver(value); }]; return promise; } 複製代碼
此處僅展現swift的語法,oc上的使用請參考FBL
前綴的類,或者直接閱讀官方文檔markdown
默認是在主線程中使用,如需在其餘線程中使用,能夠設置on: DispatchQueue
,此處不做介紹
let promise = Promise<Int> { fulfill, reject in let vail = true if vail { fulfil(12) }else { reject(error) } } 複製代碼
//採用預先定義方式 let promise = Promise<Int>.pending() ... if success { promise.fulfill(12) } else { promise.reject(error) } //直接發起結果的方式 let promise = Promise(12) let promise = Promise(error) 複製代碼
let numberPromise = Promise { fulfill, _ in fulfill(42) } let stringPromise = numberPromise.then { number in return Promise { fulfill, _ in fulfill(String(number)) } } typealias Block = (Int) -> [Int] let blockPromise = stringPromise.then { value in return Promise<Block> { fulfill, _ in fulfill({ number in return [number + (Int(value) ?? 0)] }) } } let finalPromise = blockPromise.then { (value: @escaping Block) -> Int? in return value(42).first } let postFinalPromise = finalPromise.then { number in return number ?? 0 } 複製代碼
catch是處理promise中出現錯誤的狀況,在promise管道中,當其中一個promise執行出現錯誤時,會忽略這個以後的管道直接將錯誤傳輸到catch中處理。
let promise = Promise<AnyObject> { return error }.catch { error in //處理錯誤 } 複製代碼
只有當all中全部的promise否成功執行後纔回調,回調參數未all中全部執行結果的元組,當其中一個調用rejected
時,都直接回調錯誤
let promise1 = Promise<Int?> { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 1, execute: { fulfill(42) }) } let promise2 = Promise<Int?> { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 2, execute: { fulfill(13) }) } let promise3 = Promise<Int?> { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 3, execute: { fulfill(nil) }) } let combinedPromise = all([promise1, promise2, promise3]).then { value in print(value) //[Optional(42), Optional(13), nil] } 複製代碼
不管管道中是順利成功執行仍是出現錯誤,最終都會執行always的閉包
var count = 0 let promise = Promise<Void> { _, reject in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 3, execute: { reject(DYError.promise) }) }.always { count += 1 }.catch { error in count += 1 }.always { print(count) } 複製代碼
與all
操做類型,可是Any
中即使有其中的出現了錯誤也會返回元組數據嗎,其元組類型是Maybe
,包含error
與value
相關信息
let promise1 = Promise<Int?> { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 3, execute: { fulfill(42) }) } let promise2 = Promise<Int?> { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 2, execute: { fulfill(13) }) } let promise3 = Promise<Int?> { _, reject in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 1, execute: { reject(DYError.promise) }) } let combinedPromise = any([promise1, promise2, promise3]).then { value in let item = value.first print(value.first?.value) } 複製代碼
使用該操做,能夠同步等待在不一樣線程上執行的promise。該語法與ES8中async/await
的使用時相似的
Promise<Int> { let minusFive = try await(calculator.negate(5)) let twentyFive = try await(calculator.multiply(minusFive, minusFive)) let twenty = try await(calculator.add(twentyFive, minusFive)) let five = try await(calculator.subtract(twentyFive, twenty)) let zero = try await(calculator.add(minusFive, five)) return try await(calculator.multiply(zero, five)) }.then { result in }.catch { error in } 複製代碼
該操做返回一個預先定義的promise,等到給定的時間後執行,或者當即執行錯誤
let promise = Promise(42).delay(2) promise.catch { err in print(err) }.then { value in print(value) } DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 1, execute: { promise.reject(DYError.promise) }) 複製代碼
該操做與all
類似,可是返回的是先執行完成的promise結果或者錯誤
let promise1 = Promise<Any?> { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 2, execute: { fulfill(42) }) } let promise2 = Promise<Any?> { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 3, execute: { fulfill("hello world") }) } let promise3 = Promise<Any?> { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 2, execute: { fulfill([44]) }) } let promise4 = Promise<Any?> { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 4, execute: { fulfill(nil) }) } race([promise1,promise2,promise3,promise4]).then { (value) in print(value) } 複製代碼
與Catch
效果相同,可是該操做符不會隔斷管道中其餘promise的執行
let promise = Promise<Int> { _, reject in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 4, execute: { reject(DYError.promise) }) }.recover { error -> Promise<Int> in print(error) return Promise { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 4, execute: { fulfill(1) }) } }.catch { err in print(err) }.then { value in print(value) } 複製代碼
結合其餘數據進行變換
let numbers = [1, 2, 3] Promise("0").reduce(numbers) { partialString, nextNumber in Promise(partialString + ", " + String(nextNumber)) }.then { string in // Final result = 0, 1, 2, 3 print("Final result = \(string)") } 複製代碼
promise執行失敗時,嘗試從新執行。在未設置時間的前提下,默認延遲1s從新執行
var count = 0 retry(attempts: 3, delay: 5, condition: { (num, error) -> Bool in print("\(num)" + error.localizedDescription) if num == 2 { //立刻執行錯誤 return false } return true }) { () -> Promise<Int> in return count == 3 ? Promise(42) : Promise(DYError.promise) }.then { (value) in print(value) }.catch { (error) in print(error) } 複製代碼
在指定的時間內若是沒有執行完成或者發送錯誤,則自發錯誤
let promise = Promise { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 4, execute: { fulfill(42) }) }.timeout(1).catch { err in print(err) //timedOut }.then { value in print(value) } 複製代碼
判斷是否有效,若是無效則自發錯誤
let promise = Promise { fulfill, _ in DispatchQueue(label: "queue").asyncAfter(deadline: .now() + 4, execute: { fulfill(42) }) }.validate { value in return value == 1 }.catch { err in print(err) //validationFailure }.then { value in print(value) } 複製代碼