PromiseZ 0.3.0

PromiseZ 0.3.0

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布最后发布2015年3月

Zach Radke 维护。



PromiseZ 0.3.0

  • Zach Radke

这是一个对 Promises/A+ 规范 的高层次实现,它大量借鉴了 RXPromise 实现


安装

Cocoapods 是 iOS 和 OSX 应用程序的出色依赖管理器。如果您不熟悉,请查看 Cocoapods 网站 开始使用。

在 Cocoapods 设置完成后,只需将以下内容添加到您的 Podfile 中

pod 'PromiseZ'

并且当您导入库时

#import <PromiseZ/PZPromise.h>

使用它

在其核心,Promise 代表一个未确定的结果。例如,在发起网络请求时,数据不会立即可用,请求可以成功并有一个结果,或者由于某些原因失败。Promises 通过一个对象代表所有这些状态和潜在值。

PZPromise 类遵从 <PZThenable> 协议,并符合 Promises/A+ 规范。可以通过 -init 方法进行初始化。

假设我们有一个异步处理的背景方法

- (PZPromise *)doSomethingAsync
{
    // This promise will need to be retained somehow so it can be notified of it's eventual value or failed reason.
    PZPromise *promise = [PZPromise new];
    ...
    return promise;
}

在最基本的水平上,我们可以通过添加 on-kept 和 on-broken 块来通知方法完成

PZPromise *promise = [self doSomethingAsync];
[promise thenOnKept:^id(id value) {
    // Do something with the result
    ...
    return nil; // The return value doesn't matter in this case
} onBroken:^id(NSError *reason) {
    // Do something to handle the failure
    ...
    return nil; // The return value doesn't matter in this case
}];

注意,on-kept 和 on-broken 块实际上返回一个值。这是因为 -thenOnKept:onBroken: 方法实际上返回另一个 <PZThenable>!这个新的 Promise 将根据您从 on-kept 和 on-broken 块返回的内容而定(resolve),或者在您没有提供块的情况下,根据原始 Promise 的结果而定。所以在我们这个例子中,假设我们想在成功结果上执行其他操作

- (PZPromise *)doSomethingElseAsyncWithResult:(id)result
{
    ...
}

我们不返回 nil 从我们的 on-kept 块,我们可以返回那个 Promise

PZPromise *promiseA = [self doSomethingAsync];
PZPromise *promiseB = [promise thenOnKept:^id(id value) {
    // We return the next promise we want to execute
    return [self doSomethingElseAsyncWithResult:value];
} onBroken:^id(NSError *reason) {
    // Do something to handle the failure
    ...

    // We can return an already broken promise so promiseB will also be broken.
    return [[PZPromise alloc] initWithBrokenReason:reason];
}];

在这种情况下,promiseB 将在由 doSomethingElseAsyncWithResult: 返回的 Promise 解决时解决。

但是,在我们的例子中,我们并不真正需要 promiseA,所以我们忽略它并开始链式调用

PZPromise *promise = [[self doSomethingAsync] thenOnKept:^id(id value) {
    return [self doSomethingElseAsyncWithResult:value];
} onBroken:^id(NSError *reason) {
    ...
    return [[PZPromise alloc] initWithBrokenReason:reason];
}];

返回的 Promise 只有在 -doSomethingAsync 解决其 Promise 并 -doSomethingElseAsyncWithResult: 解决后才会解决。只要我们需要,我们就可以继续链式调用

PZPromise *promise = [[[self doSomethingAsync] thenOnKept:^id(id value) {
    return [doSomethingElseAsyncWithResult:value];
} onBroken:nil] thenOnKept:^id(id value) {
    return [doAnotherThingWithAnotherResult:value];
} onBroken:nil] thenOnKept:^id(id value) {
    return [doFinalThingWithFinalResult:value];
} onBroken:^id(NSError *error) {
    // Handle any of the errors that other promises in the chain encountered
    ...
    return [[PZPromise alloc] initWithBrokenReason:reason];
}];

请注意,我们为 on-broken 块传递了 nil。记住,on-kept 和 on-broken 块都是可选的。如果它们是 nil,Promise 会简单地将其状态和值传递给返回的 Promise。这样,最后的 on-broken 块实际上会捕获之前任何 Promise 的失败!对于 on-kept 块也是如此。

-thenOnKept:onBroken: 方法返回的 Promise 与通过 -init+new 创建的 Promise 不同,因为它们的解析取决于初始 Promise 或块的返回值。因此,它们被认为是“已绑定”的,并且 已绑定的 Promise 不能手动保留或中断。在绑定的 Promise 上调用 -keepWithValue:-breakWithReason: 将没有作用。

有什么问题吗?

PZPromise 类只表示 Promise 等式的一部分。另一部分是,为了使其有用,您的异步方法必须生成、返回、保留并中断 PZPromise 示例。具体的实现略超出了本说明文件的范畴(尽管您可以查看示例应用程序以获得一些启发),但通常有几个需要考虑的点

  • 当调用异步方法时,返回一个未完成的 PZPromise 实例。
  • 如果异步方法成功解析,则在 Promise 上调用 -keepWithValue:,并传入成功的结果。
  • 如果异步方法由于某些原因失败,则在 Promise 上调用 -breakWithReason:,并传入表示原因的 NSError。

实现中的关键点在于具有异步方法的需要保留其 Promise 并用正确的值(成功或失败)解决正确的 Promise。一旦掌握了这个,一切都很顺利!


深入了解

高层次概述不够吗?本节将深入探讨有关 PZPromise 实现的一些更微妙的问题。

成为“thenable”

<PZThenable> 协议定义了 -thenOnKept:onBroken: 方法,该方法的违规者必须实现。虽然 PZPromise 符合此协议,但在需要返回自定义对象而不是常规 PZPromise 的情况下,它被保留为独立状态。尽管如此,通常 PZPromise 类应该足够。

并发

  • PZPromise 应该是线程安全的,可以在任何线程中解决(调用 -keepWithValue:-breakWithReason:),无论它们的创建位置如何。
  • 根据 Promises/A+ 规范,保留或中断块始终在至少下一个运行循环上异步执行,即使是接收到的 PZPromise 已经被保留或中断。
  • 保留和中断块不对它们将被调用在哪个线程上作出保证。因此,当进行 UI 更改时,始终将它们调度回主线程是非常重要的。