写在前面
在iOS中我们一般使用delegate(代理)或者block(闭包)来进行异步操作,当要执行多个异步操作,必须将第二个嵌套在第一个的完成内,并且还要正常处理错误。这使得代码结构异常的混乱,不方便查看。
相信码过JS的同学都清楚,在es6中新增promise的语法,从此异步操作更加的灵活,它使得回调与事件进行了组合,而且写法优美。谷歌在18年上半年开源了promises库,使iOS上的异步编程更加便捷,其使用方式与JS中的promise一致,并支持OC与Swift。
介绍
Promise代表异步执行的结果或者错误信息。其拥有以下三种状态
- pending 正在处理
- fulfilled 处理完成得到的结果
- rejected 发生错误的信息
当状态发生变更后,就无法再次变更其状态。同时promise不限制其订阅者,当状态发生变化后,所有的订阅者都能接收到通知。同时订阅者可以返回另一个promise对象,从而形成管道通信。
管道通信过程可以自由的使用线程
原理分析
我们可以在FBLPromise文件查找Promise的实现原理
声明状态、闭包
//三种状态类型
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;
}
完成或失败的操作
完成或失败的操作其实是遍历所有订阅者,将当前的状态和执行后的结果通知订阅者。执行完毕后改变缓存的状态值,就算后期再次调用也不会响应。达到只要状态变更就无法再次启动的效果。
- (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的原理,也是实现管道通信的原理
- (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前缀的类,或者直接阅读官方文档
创建方式
默认是在主线程中使用,如需在其他线程中使用,可以设置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)
then使用
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
catch是处理promise中出现错误的情况,在promise管道中,当其中一个promise执行出现错误时,会忽略这个之后的管道直接将错误传输到catch中处理。
let promise = Promise<AnyObject> {
return error
}.catch { error in
//处理错误
}
操作符
All
只有当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
无论管道中是顺利成功执行还是出现错误,最终都会执行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)
}
Any
与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)
}
Await
使用该操作,可以同步等待在不同线程上执行的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
}
Delay
该操作返回一个预先定义的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)
})
Race
该操作与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)
}
Recover
与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)
}
Reduce
结合其他数据进行变换
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)")
}
Retry
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)
}
Timeout
在指定的时间内如果没有执行完成或者发送错误,则自发错误
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)
}
Validate
判断是否有效,如果无效则自发错误
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)
}