受Promises/A启发,经过一定修改和添加,以更好地适应常见的Objective-C模式的一个经过测试和完全文档化的promises库。
如果您对promises一无所知,我建议您阅读一些文章和教程,例如1、2、3或4。一些方法名称可能不同,但基本的思路大致相同。
以下是OMPromises的主要特性。
then:
、rescue:
、fulfilled:
、failed:
和progressed:
,类似于大多数其他库安装OMPromises的推荐方法是使用CocoaPods包管理器。
pod 'OMPromises', '~> 0.8.1'
所有公共类、方法和属性都进行了文档化,因此OMPromises的每个副本都包含相应的头文件中的完整文档。
使用appledoc渲染的在线版本,阅读起来更方便,可以在这里找到。
Promises由类型为OMPromise
的对象表示。创建已经实现、失败或延迟的promises,可以使用以下静态方法之一。
OMPromise *promise = [OMPromise promiseWithResult:@1337];
// promise.state == OMPromiseStateFulfilled
// promise.result == @1337
OMPromise *promise1SecLate = [OMPromise promiseWithResult:@1338 after:1.f];
// promise1SecLate.state == OMPromiseStateUnfulfilled
// promise1SecLate.result == nil
// ... after 1 second ..
// promise1SecLate.state == OMPromiseStateFulfilled
// promise1SecLate.result == @1338
OMPromise *failed = [OMPromise promiseWithError:[NSError ...]];
// failed.state == OMPromiseStateFailed
// failed.error == [NSError ...]
// To delay the fail use promiseWithError:after: similar to promiseWithResult:after:
Promises的一个特殊属性是,它们只能从未实现状态到失败或实现状态进行一次状态转换。承诺本身不提供进行此类转换的接口。这就是为什么存在一个称为OMDeferred
的父类的原因,它产生一个承诺,是唯一可以改变相应承诺状态的权利机构。
因此,在上面的示例中使用的承诺是只读的。要创建承诺,你可能实际上需要创建一个异步任务,你应该为自己保留它,并返回一个与新创建的异步任务对应的承诺。为了保持对承诺的了解,你可能可以使用progress:
多次,然后最多使用一次在异步任务上的fulfil:
或fail:
。
- (OMPromise *)workIntensiveButSynchronousMethod {
OMDeferred *deferred = [OMDeferred new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do your long-running task, eventually provide information about the progress..
while (running)
[deferred progress:progress];
if (failed) {
[deferred fail:error];
} else {
[deferred fulfil:result];
}
});
return deferred.promise;
}
如果你不需要进度,你也可以简化上面的代码片段,如下所示
- (OMPromise *)workIntensiveButSynchronousMethod {
return [OMPromise promiseWithTask:^{
// do the long running task
// ...
// once we are done, we return the result, an NSError is automatically
// treated as failure
return (failed) ? error : result;
} on:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
}
一旦你获得了OMPromise
的实例,你可能想对状态变化做出反应。这是通过注册块来完成的,使用的方法是fulfilled:
、failed:
和/或progressed:
,如果发生了相应的事件,这些方法会被调用。这些方法返回self
以简单地链接多个调用,并允许注册对同一事件的多个回调。
[[OMPromise promiseWithResult:@1337 after:1.f]
fulfilled:^(id result) {
// called after 1 second, result == @1337
}];
OMPromise *networkRequest = [OMHTTPRequest get:@"http://google.com"
parameters:nil
options:nil];
[[[networkRequest
fulfilled:^(OMHTTPResponse *response) {
// called if the network request succeeded
}]
failed:^(NSError *error) {
// otherwise ...
}]
progressed:^(float progress) {
// describes the progress as a value between 0.f and 1.f
}];
有时可能需要链式调用承诺,并通过一个承诺本身来表示链。例如,创建由承诺描述的小部分,并使用某些方法将这些小部分组合成更大的整体。
通过使用名为then:
的方法实现所描述的行为。类似于fulfil:
,它会执行一个在承诺得到解决后被执行的块,但该块必须返回一个值。这个值可能是一个新的承诺或任何其他对象,然后它被绑定到由then:
返回的新创建的承诺。如果链中的任何承诺失败,则整个链都会失败,并且会跳过所有连续的then块。
// get User by its email
- (OMPromise *)getUserByEmail:(NSString *email);
// get all Comments created by a User
- (OMPromise *)getCommentsOfUser:(User *)user;
// get all Comment messages by a User having a certain email address
- (OMPromise *)getCommentsByUsersEmail:(NSString *)email {
OMPromise *chain = [[[self getUserByEmail:email]
then:^id(User *user) {
// we can chain by returning other promises
return [self getCommentsOfUser:user];
}]
then:^id(NSArray *comments) {
// but also any other object to perform e.g. value transformations
NSMutableArray *commentMessages = [NSMutableArray array];
for (Comment *comment in comments) {
[commentMessages addObject:comment.message];
}
return commentMessages;
}];
// chain fails if either the first promise, returned by getUserByEmail:,
// or the second once, returned by getCommentsOfUser:, fails;
// otherwise the results are propagated from promise to promise until the
// last promise, representing the chain, is fulfilled
return chain;
}
在某些情况下,可能需要从失败中恢复并让链继续,就好像一切都没有发生错误一样。这就是rescue:
发挥作用的时候。它返回一个新创建的承诺,但是提供给它的块如果在承诺失败时会调用。
// yields an UIImage if the user supplied an avatar
- (OMPromise *)getAvatarForUser:(User *)user;
// always returns an UIImage being either the User's supplied one or a dummy image
- (OMPromise *)getAvatarOrDummyForUser:(User *)user {
OMPromise *chain = [[self getAvatarForUser:user]
rescue:^id(NSError *error) {
return [UIImage imageNamed:@"dummy_image.png"];
}];
// chain gets fulfilled always, due to rescue:
return chain;
}
此时,有些人可能会认识到这与范畴论中单子的相似之处。如果不熟悉,你可以查阅Haskell及其概念。
如果你抽象的操作很重且运行时间很长,你可能需要支持取消。它允许承诺的所有者取消
承诺,从而取消长时间运行的操作(如果可能的话)。一旦取消承诺,它就会进入失败状态,并带有特定的错误代码。
默认情况下,承诺不可撤销,但异步任务的所有者可以通过使用cancelled:
注册至少一个取消处理器来使其可行。根据任务和用例是否需要实现取消处理器,这是有意义的。
- (OMPromise *)get100GofData {
OMDeferred *deferred = [OMDeferred new];
[deferred cancelled:^(OMDeferred *this) {
// cancel the download..
}];
return deferred.promise;
}
- (void)startDownloadAndAbort {
OMPromise *dl = [self get100GofData];
// dl.cancellable == YES
// ...
// for an unknown reason we dont need it anymore
[dl cancel];
// if cancelled, it switches into the failed state
[dl failed:^(NSError *error) {
// error.domain == OMPromisesErrorDomain
// error.code == OMPromisesCancelledError
}];
}
目前仅提供以下组合器,请查阅文档获取更详细的信息。每个组合器都假设提供给所有承诺的工作负载均匀分配,从而创建合理的进度组合。
join
- 移除一层承诺封装chain:initial:
- 等同于应用多个链式/回调调用all:
- 等待所有承诺得到解决,如果有任何承诺失败,则失败。any:
- 如果提供的任何一个promise被满足,则返回满足的结果,否则失败。collect:
- 收集所需承诺的全部结果,因此它永远不会失败。relay:
- 将所有承诺事件转发到另一个延迟承诺。OMPromises遵循MIT许可证条款。请查阅LICENSE文件以获取详细信息。