Promise在web前端的开发工程师中使用的非常频繁,通过then使同步的编程思路来进行异步调用,使得异步编码变得非常的简单。iOS也有PromiseKit这样的开源库可以使用,不过自己实现一个使用起来会更方便和贴近。
web端的Promise使用
//第一个异步任务
function run_a(){
return new Promise(function(resolve, reject){
//假设已经进行了异步操作,并且获得了数据
resolve("step1");
});
}
//第二个异步任务
function run_b(data_a){
return new Promise(function(resolve, reject){
//假设已经进行了异步操作,并且获得了数据
console.log(data_a);
resolve("step2");
});
}
//第三个异步任务
function run_c(data_b){
return new Promise(function(resolve, reject){
//假设已经进行了异步操作,并且获得了数据
console.log(data_b);
resolve("step3");
});
}
//连续调用
run_a().then(function(data){
return run_b(data);
}).then(function(data){
return run_c(data);
}).then(function(data){
console.log(data);
});
/*运行结果
step1
step2
step3
*/
参考上面的例子,对Promise稍微有些了解的应该都知道Promise包装了一个异步调用并生成一个Promise实例,当异步调用返回的时候根据调用的结果分别调用实例化时传入的resolve 和 reject方法,这个时候then就会接收到对应的数据,这个是Promise最基本使用,当然真正的Promise还包含了很多其它的使用,这里我就使用OC实现最基本的异步调用then功能。
Objective C 的实现
实现代码放在github上,只有一个类文件可以随意的copy到工程中:https://github.com/MathewWang/WWPromise
如何使用
// Resolve Example
WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
resolve(@"aaa");
});
}).then(^(NSString *result){
NSLog(@"###promise the result is 111: %@", result);
return WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
resolve([result stringByAppendingString:@"bbb"]);
});
});
}, nil).then(^(NSString *result){
NSLog(@"###promise the result is 222: %@", result);
return [WWPromise empty];
}, nil);
// Reject Example
WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
reject(@"aaa");
});
}).then(nil, ^(NSString *result){
NSLog(@"###promise the result is 111: %@", result);
return [WWPromise empty];
});
参照上面的例子,在添加then的时候,对于resolve 和 reject的回调函数,如果不需要直接传入nil即可,不需要返回参数的then需要返回一个[WWPromise empty]。
One More
因为Promise在实例化的时候就开始执行异步调用了,这点和RAC的冷信号不一样(冷信号是订阅时触发异步调用的),因此我们可以将Promise再进行一次封装使得对于OC的开发更加友好,这也是我们业务中真正使用的样子:
实现一个抽象类“AbstractPromiseExecution”
typedef void(^ExecutionSuccess)(id result);
typedef void(^ExecutionFailed)(NSError *error);
@interface AbstractPromiseExecution()
@property (nonatomic, strong) ExecutionSuccess success;
@property (nonatomic, strong) ExecutionFailed failed;
@end
@implementation AbstractPromiseExecution
- (WWPromise *(^)(id))promise{
return ^(id bizDatas){
return WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
//设置success
self.success = ^(id result) {
resolve(result);
};
//设置failed
self.failed = ^(NSError *error) {
reject(error);
};
[self startExecute:bizDatas];
});
};
}
- (void)startExecute:(id) bizDatas{
//override by subclass
}
从上面的抽象类很明显的看出来,- promise方法会返回一个Promise实例,将业务的success和failed回调赋值,同时会执行模板方法- (void)startExecute:(id) bizDatas将需要传递的业务数据bizDatas传递下去,因此业务类只要继承AbstractPromiseExecution然后复写- (void)startExecute:(id) bizDatas方法,在其中添加自己的业务逻辑,成功后调用 success 或者 failed,事例代码如下:
//子类 1
@interface FetchUserGroupInfoExecution : AbstractPromiseExecution
@end
@implementation FetchUserGroupInfoExecution
- (void)startExecute:(id) bizDatas{
//做异步请求
[UserService requestUserGroup:^(id result){
strongSelf.success(result);
} error:^(NSError *errorInfo){
strongSelf.failed(errorInfo);
}];
}
@end
//子类 2
@interface CheckGroupAuthrizationExecution : AbstractPromiseExecution
@end
@implementation CheckGroupAuthrizationExecution
- (void)startExecute:(id) bizDatas{
//做异步请求
[UserGroupService checkGroupAuthrization:^(id result){
strongSelf.success(result);
} error:^(NSError *errorInfo){
strongSelf.failed(errorInfo);
}];
}
@end
这两个子类是先获取用户的组信息,然后在获取这个组的权限信息,在业务使用时候如下:
FetchUserGroupInfoExecution *fetchExecution = [FetchUserGroupInfoExecution new];
CheckGroupAuthrizationExecution *checkExecution = [CheckGroupAuthrizationExecution new];
fetchExecution.promise(@{@"userId":@123})
.then(^(id res){
//Do business
return checkExecution.promise(res);
},^(NSError *error){
//Do error handling
}
).then(^(id res){
//Do business
return [WWPromise empty];
},^(NSError *error){
//Do error handling
}
);
通过上面这样的简单封装,以后的业务扩展也是非常的容易,编写也很简单,子类只要关心传进来的数据做与自己相关的业务处理即可。